最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

源碼解析Android Jetpack組件之ViewModel的使用

 更新時間:2023年04月21日 15:27:30   作者:孫先森Blog  
Jetpack 是一個豐富的組件庫,它的組件庫按類別分為 4 類,分別是架構(Architecture)、界面(UI)、 行為(behavior)和基礎(foundation)。本文將從源碼和大家講講Jetpack組件中ViewModel的使用

前言

在之前 LiveData 源碼淺析的博客中提到了 ViewModel 組件,當時對 ViewModel 的解釋是 “生命周期比Activity” 更長的對象。本文就來了解下其實現(xiàn)原理。

依賴版本

// 注意這里的 appcompat、activity-ktx、fragment-ktx
// 高版本的自動引入了 viewmodel-savedstate 實戰(zhàn)中很少用到的功能
// 篇幅原因 就不再本文中分析 viewmodel-savedstate 擴展組件了
implementation 'androidx.appcompat:appcompat:1.0.0'

def fragment_version = "1.1.0"
def activity_version = "1.0.0"
implementation "androidx.activity:activity-ktx:$activity_version"
implementation "androidx.fragment:fragment-ktx:$fragment_version"

def lifecycle_version = "2.5.1"

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"

基礎使用

定義

class MainViewModel: ViewModel(){ ... }
// or 
class MainViewModel(application: Application): AndroidViewModel(application){
    val data: String = ""
    
    fun requestData(){
        data = "xxx"
    }
}

在 MainVieModel 中可以定義 UI 界面中需要的數(shù)據(對象、LiveData、Flow 等等)和方法,在 Activity 真正銷毀前 ViewModel 中的數(shù)據不會丟失。

Activity 中獲取

val vm = ViewModelProvider(this).get(MainViewModel::class.java)
// or
// 引入 activity-ktx 庫可以這樣初始化 ViewModel
val vm by viewModels<MainViewModel>()

// 通過 vm 可以調用其中的方法、獲取其中的數(shù)據
vm.requestData()
Log.e(TAG, vm.data)

Fragment 中獲取

val vm = ViewModelProvider(this).get(MainViewModel::class.java)
// or 
// 獲取和 Activity 共享的 ViewModel 也就是同一個 ViewModel 對象
val vm = ViewModelProvider(requireActivity()).get(MainViewModel::class.java)

引入 fragment-ktx 可以這樣初始化

val vm = viewModels<MainViewModel>()
// or 效果同上
val vm = activityViewModels<MainViewModel>()

前置知識

ViewModel 的使用非常簡單,也很容易理解,就是一個生命周期長于 Activity 的對象,區(qū)別在于不會造成內存泄漏。ViewModel 不是魔法,站在開發(fā)者的角度在 ViewModel 沒有問世之前橫豎屏切換需要保存狀態(tài)數(shù)據的需求通常都是通過 onSaveInstanceState、onRestoreInstanceState 來實現(xiàn)。

onSaveInstanceState、onRestoreInstanceState

關于這兩個方法這里就簡單概述一下:onSaveInstanceState 用于在 Activity 橫豎屏切換(意外銷毀)前保存數(shù)據,而 onRestoreInstanceState 是用于 Activity 橫豎屏切換(重建)后獲取保存的數(shù)據;

onSaveInstanceState 調用流程

由于是在 Activity 銷毀前觸發(fā),那么直接來 ActivityThread 中找到 performPauseActivity 方法:

ActivityThread.java

private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason, PendingTransactionActions pendingActions) {
    // ...
    if (shouldSaveState) {
        callActivityOnSaveInstanceState(r);
    }
    // ...
}

private void callActivityOnSaveInstanceState(ActivityClientRecord r) {
    // ...
    // 這里通過 ActivityClientRecord 獲取到 activity
    // state 是 Bundle 對象,后面要保存的數(shù)據就放在 state 中
    mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
    // ...
}

這里有 ActivityThread 調用到了 Instrumentation 中,繼續(xù)看源碼:

Instrumentation.java

public void callActivityOnSaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
    activity.performSaveInstanceState(outState);
}

根據傳入的 activity 調用其 performSaveInstanceState 方法:

Activity.java

final void performSaveInstanceState(@NonNull Bundle outState) {
    onSaveInstanceState(outState);
}

總結一下,onSaveInstanceState 中我們將數(shù)據存儲在 Bundle 對象中,而這個 Bundle 對象是存儲在 ActivityClientRecord 中。

onRestoreInstanceState 調用流程

看完了 onSaveInstanceState 的調用流程,那么 onRestoreInstanceState 的流程就來簡單說說,由于在 onStart 后發(fā)生回調,所以直接去看 ActivityThread 中的源碼:

ActivityThread.java

public void handleStartActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, ActivityOptions activityOptions) {
    // ...
    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
    // ...
}

可以看出這里從 ActivityClientRecord 中取出了 activity 和 state 進行傳毒,后面就和 onSaveInstanceState 調用流程一樣了,源碼比較簡單就不貼了。

onRetainCustomNonConfigurationInstance、getLastCustomNonConfigurationInstance

除了 onSaveInstanceState 和 onRestoreInstanceState,在 Activity 中還有一組方法可以實現(xiàn)類似的功能,就是 onRetainCustomNonConfigurationInstance 和 getLastCustomNonConfigurationInstance,前者即保存數(shù)據,后者即獲取保存的數(shù)據;

簡單使用

override fun onRetainCustomNonConfigurationInstance(): Any? {
    val data = SaveStateData()
    return data
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // 獲取保存的數(shù)據
    val data = getLastCustomNonConfigurationInstance() as SaveStateData
}

和 onSaveInstanceState 使用的區(qū)別在于 onSaveInstanceState 只能在其參數(shù)中的 Bundle 對象中寫入數(shù)據,而 onRetainCustomNonConfigurationInstance 返回的類型是 Any(Java Object)不限制數(shù)據類型。老樣子看一下這組方法的源碼調用流程。

onRetainCustomNonConfigurationInstance

onRetainCustomNonConfigurationInstance 是在 ComponentActivity 中定義的,默認實現(xiàn)返回 null,其在 onRetainNonConfigurationInstance 方法中被調用:

ComponentActivity.java

public Object onRetainCustomNonConfigurationInstance() {
    // ComponentActivity 中默認返回 null
    return null;
}

public final Object onRetainNonConfigurationInstance() {
    // 保存在了 custom 變量中
    Object custom = onRetainCustomNonConfigurationInstance();
    // 這里已經出現(xiàn) ViewModel 相關的源碼了,這里先按下不表
    ViewModelStore viewModelStore = mViewModelStore;
    if (viewModelStore == null) {
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            viewModelStore = nc.viewModelStore;
        }
    }

    if (viewModelStore == null && custom == null) {
        return null;
    }
    // 新建 NonConfigurationInstances 對象
    NonConfigurationInstances nci = new NonConfigurationInstances();
    // custom 賦值給了 NonConfigurationInstances 對象
    nci.custom = custom;
    nci.viewModelStore = viewModelStore;
    return nci;
}

從 ComponentActivity 的這部分源碼中可以看出保存的數(shù)據最終放在了 NonConfigurationInstances 對象的 custom 屬性中;接著找 onRetainNonConfigurationInstance 的定義,在 Activity 中:

Activity.java

public Object onRetainNonConfigurationInstance() {
    // 默認返回 null
    return null;
}

NonConfigurationInstances retainNonConfigurationInstances() {
    // ComponentActivity 中返回的 NonConfigurationInstances 對象
    Object activity = onRetainNonConfigurationInstance();
    // ...
    // 注意 這里有新建另一個 NonConfigurationInstances 對象
    NonConfigurationInstances nci = new NonConfigurationInstances();
    // ComponentActivity 中返回的 NonConfigurationInstances 對象
    // 存儲到了新的 NonConfigurationInstances 中的 activity 屬性中
    nci.activity = activity;
    // ...
    return nci;
}

在 Activity 類中相當于做了一層套娃,又新建了一個 NonConfigurationInstances 對象,將 ComponentActivity 中返回的 NonConfigurationInstances 對象存了進去;

其實源碼看到這里就可以了,不過本著刨根問底的原則,我們接著再看一下 NonConfigurationInstances 到底存在了哪里?在 ActivityThread.java 中找到了調用 retainNonConfigurationInstances 的地方:

ActivityThread.java

void performDestroyActivity(ActivityClientRecord r, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) {
    // ...
    // 這個 r 是參數(shù)中的 ActivityClientRecord
    r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
}

和 onSaveInstanceState 一樣存儲在了 ActivityClientRecord 中,只不過換了一個屬性罷了。

getLastCustomNonConfigurationInstance

看完了存儲的流程,簡單來看看取數(shù)據的流程。既然存的時候套娃了一下 NonConfigurationInstances,那取數(shù)據的時候肯定也需要套娃:

ComponentActivity.java

public Object getLastCustomNonConfigurationInstance() {
    // 通過 getLastNonConfigurationInstance 獲取 NonConfigurationInstances
    NonConfigurationInstances nc = (NonConfigurationInstances)
            getLastNonConfigurationInstance();
    // 返回 custom
    return nc != null ? nc.custom : null;
}

那么在 Activity 中肯定還需要取一次 ActivityClientRecord 中的 NonConfigurationInstances:

Activity.java

NonConfigurationInstances mLastNonConfigurationInstances;

public Object getLastNonConfigurationInstance() {
    // 返回其 activity 字段
    return mLastNonConfigurationInstances != null
            ? mLastNonConfigurationInstances.activity : null;
}

// mLastNonConfigurationInstances 賦值在 attach 方法中
final void attach(Context context, /*參數(shù)太多 省略了*/ NonConfigurationInstances lastNonConfigurationInstances) {
    // ...
    mLastNonConfigurationInstances = lastNonConfigurationInstances;
    // ...
}

可以看出在 Activity attach 方法中就已經拿到了套娃后的 NonConfigurationInstances 對象,我們都知道 Activity attach 方法是在 ActivityThread 的 performLaunchActivity 中調用,看一下源碼:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    // ...
    // 參數(shù)太多 省略了
    // 可以看到是從 ActivityClientRecord 中取出傳入的
    activity.attach(appContext, r.lastNonConfigurationInstancesn);
    // ...
}

小節(jié)總結

兩種方式都是將數(shù)據保存到了 ActivityClientRecord 中,不同的是前者限制了 Bundle 類型,后者不限制類型(ViewModel 采用的就是后者這組方法實現(xiàn)),不過后者已經在源碼中被標記了刪除,并不影響使用,標記刪除是為了讓開發(fā)者們利用 ViewModel 來接管這種需求。下面我們就正式進入 ViewModel 源碼。

源碼分析

前置知識有點長,不過也幾乎把 ViewModel 的原理說透了,ViewModel 的保存、恢復是利用了系統(tǒng)提供的方法,不過還有些細節(jié)還需要在源碼中探索,比如:如何實現(xiàn) Activity/Fragment 共享 ViewModel?接下來就來深入 ViewModel 源碼。

創(chuàng)建

先來以 Activity 中創(chuàng)建 ViewModel 的這段代碼入手:

val vm by viewModels<MainViewModel>()

查看 viewModels 源碼:

// 這是一個 ComponentActivity 的擴展方法
@MainThread // 在主線程中使用
inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
    // 從命名也可以看出是一個工廠模式,默認是 null
    noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
    // 默認 factoryProducer 為 null
    // 返回的是 AndroidViewModelFactory
    val factoryPromise = factoryProducer ?: {
        val application = application ?: throw IllegalArgumentException(
            "ViewModel can be accessed only when Activity is attached"
        )
        AndroidViewModelFactory.getInstance(application)
    }
    
    // 返回了一個 ViewModelLazy 對象,將 viewModelStore、factoryProducer 傳入
    return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}

到這里先暫??匆幌?AndroidViewModelFactory 是如何初始化的,以及 viewModelStore 是什么東東:

ViewModelProvider.kt

private var sInstance: AndroidViewModelFactory? = null

@JvmStatic
public fun getInstance(application: Application): AndroidViewModelFactory {
    if (sInstance == null) {
        sInstance = AndroidViewModelFactory(application)
    }
    return sInstance!!
}

是一個單例模式,直接對 AndroidViewModelFactory 進行實例化,再來看看 mViewModelStore

ComponentActivity.java

// 都是定義在 ComponentActivity 中的變量,默認 null
private ViewModelStore mViewModelStore;

public ViewModelStore getViewModelStore() {
    // ...
    if (mViewModelStore == null) { // 第一次啟動 activity 為 null
        // 獲取保存的數(shù)據
        NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
         // 優(yōu)先從保存的數(shù)據中獲取
        if (nc != null) {
            mViewModelStore = nc.viewModelStore;
        }
        // 默認返回 ViewModelStore
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
    return mViewModelStore;
}

ViewModelStore 內部僅僅是管理一個 Map<String, ViewModel>,用于緩存、清理創(chuàng)建的 ViewModel。

回過頭接著看擴展方法 viewModels 返回的 ViewModelLazy:

public class ViewModelLazy<VM : ViewModel> @JvmOverloads constructor(
    private val viewModelClass: KClass<VM>, // ViewModel 的 class
    private val storeProducer: () -> ViewModelStore, // 默認是 ViewModelStore
    private val factoryProducer: () -> ViewModelProvider.Factory, // 這里就是 mDefaultFactory
    private val extrasProducer: () -> CreationExtras = { CreationExtras.Empty } // 
) : Lazy<VM> { // 注意這里返回的 Lazy,延遲初始化
    private var cached: VM? = null

    override val value: VM
        get() { // 由于返回的是 Lazy,也就是當使用 ViewModel 時才會調用 get
            val viewModel = cached
            return if (viewModel == null) { // 第一次調用是 null,進入 if
                val factory = factoryProducer() // mDefaultFactory
                val store = storeProducer() // ViewModelStore
                ViewModelProvider( // 生成 ViewModelProvider 對象
                    store,
                    factory,
                    extrasProducer()
                ).get(viewModelClass.java).also { // 調用其 get 方法獲取 ViewModel
                    cached = it  // 保存到 cached 變量
                } 
            } else {
                viewModel
            }
        }
    
    override fun isInitialized(): Boolean = cached != null
}

這里又出現(xiàn)了一個陌生的對象 CreationExtras,其內部也是一個 map,可以理解為一個鍵值對存儲對象,只不過他的 Key 是一個特殊類型。

接著查看 ViewModelProvider 的 get 方法是如何創(chuàng)建 ViewModel 的:

// 存儲ViewModel的key的前綴
internal const val DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"

public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {
    val canonicalName = modelClass.canonicalName
        ?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
    // 調用重載方法,拼接 key 傳入
    // 當前key即為:androidx.lifecycle.ViewModelProvider.DefaultKey$com.xxx.MainViewModel
    return get("$DEFAULT_KEY:$canonicalName", modelClass)
}

@MainThread
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
    val viewModel = store[key] // 優(yōu)先從 ViewModelStroe 中獲取緩存
    if (modelClass.isInstance(viewModel)) { // 如果類型相同 直接返回
        // 這里我們的 factory 是 AndroidViewModelFactory 所以不會走這行代碼
        (factory as? OnRequeryFactory)?.onRequery(viewModel)
        return viewModel as T
    }
    // ...
    // 這里的 defaultCreationExtras 是上一步驟中的 CreationExtras,默認值為 CreationExtras.Empty
    // MutableCreationExtras 包裝一層就是將 defaultCreationExtras 中所有的鍵值對都copy一份
    val extras = MutableCreationExtras(defaultCreationExtras)
    // 將當前 ViewModel 的 key 存儲進去
    extras[VIEW_MODEL_KEY] = key
    
    return try {
        // 優(yōu)先調用雙參數(shù)方法
        factory.create(modelClass, extras)
    } catch (e: AbstractMethodError) {
        // 調用雙參數(shù)方法發(fā)生異常再調用單參數(shù)方法
        factory.create(modelClass)
    }.also { 
        // 獲取到 ViewModel 后存儲到 viewModelStore 中
        // 再提一嘴 viewModelStore 是在 ComponentActivity 中定義 
        store.put(key, it) 
    }
}

終于到了創(chuàng)建 ViewModel 的部分了,直接去看 AndroidViewModelFactory 的 create 方法:

ViewModelProvider.kt

override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
    // application 不為 null 調用單參數(shù)方法
    // 在新建 AndroidViewModelFactory 已經傳入了 application,一般情況不為 null
    return if (application != null) { 
        create(modelClass)
    } else { 
        // application 如果為 null,則會從傳入的 extras 中嘗試獲取
        val application = extras[APPLICATION_KEY]
        if (application != null) {
            // 這個 create 也是雙參數(shù),但不是遞歸,第二個參數(shù)是 application,源碼貼在下面
            create(modelClass, application)
        } else {
            // 如果 application 仍然為 null,且 ViewModel 類型為 AndroidViewModel 則拋異常
            if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
                throw IllegalArgumentException(...)
            }
            // 類型不是 AndroidViewModel 則根據 class 創(chuàng)建
            // 注意這里調用的 super.create 是父類方法
            // 父類方法直接根據 modelClass.newInstance() 創(chuàng)建,就一行就不貼源碼了
            super.create(modelClass)
        }
    }
}

override fun <T : ViewModel> create(modelClass: Class<T>): T {
    return if (application == null) { // application 為 null 直接拋異常
        throw UnsupportedOperationException(...)
    } else {
        // 調用下面的雙參數(shù)方法
        create(modelClass, application)
    }
}

private fun <T : ViewModel> create(modelClass: Class<T>, app: Application): T {
    // 如果是 AndroidViewModel 類型則獲取帶 application 的構造參數(shù)創(chuàng)建
    return if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
        modelClass.getConstructor(Application::class.java).newInstance(app)
    } else {
        // 直接調用父類 create 方法通過 modelClass.newInstance() 創(chuàng)建
        super.create(modelClass)
    }
}

至此 Activity 中的 ViewModel 創(chuàng)建過程源碼就全部分析完了,總結一下:Activity 中的 ViewModel 創(chuàng)建都是通過單例工廠 AndroidViewModelFactory 的 create 方法中反射創(chuàng)建,在調用 create 創(chuàng)建前會生成字符串 key,創(chuàng)建完成后會將 key 和 vm 對象存儲到 ViewModelStore 中,后續(xù)獲取將優(yōu)先從 ViewModelStore 緩存中獲取。

ViewModelStore 是定義在 ComponentActivity 中的,ViewModel 生命周期 “長于” Activity 的原理跟這個 ViewModelStore 脫不了干系。

恢復

前面小節(jié)提過,ViewModel 的恢復利用的是 onRetainNonConfigurationInstance 方法,ViewModelStore 又是定義在 ComponentActivity 中,那么直接去看 ComponentActivity 這部分的源碼:

ComponentActivity.java

public final Object onRetainNonConfigurationInstance() {
    // 留給開發(fā)者使用的字段
    Object custom = onRetainCustomNonConfigurationInstance();
    
    // 獲取當前 Activity 的 mViewModelStore
    ViewModelStore viewModelStore = mViewModelStore;
    if (viewModelStore == null) { 
        // 如果為 null 則嘗試獲取上一次保存的數(shù)據
        NonConfigurationInstances nc =  (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // 獲取上一次存儲的 viewModelStore
            viewModelStore = nc.viewModelStore;
        }
    }
    // ...
    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom; // 開發(fā)者用的字段
    nci.viewModelStore = viewModelStore; // 保存 viewModelStore 的字段
    return nci;
}

再來看一看 ViewModelStore 的獲取方法:

public ViewModelStore getViewModelStore() {
    // ...
    if (mViewModelStore == null) {
        NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
         // 優(yōu)先從保存的數(shù)據中獲取 viewModelStore
        if (nc != null) {
            mViewModelStore = nc.viewModelStore;
        }
        // 獲取不到才會新建
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
    return mViewModelStore;
}

Activity 獲取 mViewModelStore 時優(yōu)先從 getLastNonConfigurationInstance 獲取到 NonConfigurationInstances 對象,再從其中獲取 viewModelStore,這樣在當前 Activity 作用域中創(chuàng)建過的 ViewModel 都存儲在 ViewModelStore 中,當需要再次使用時走 ViewModel 創(chuàng)建流程會直接從 ViewModelStore 中返回。

最后

再了解了 onRetainNonConfigurationInstance 這組方法之后再來探究 ViewModel 的恢復原理就很簡單了,onRetainNonConfigurationInstance 也被標記為了刪除,google 也希望開發(fā)者盡可能的使用 ViewModel 來保存數(shù)據(臨時數(shù)據)。

onRetainNonConfigurationInstance 雖然被標記為刪除,但仍然可以正常使用,相比于 onSaveInstanceState 沒有了數(shù)據類型限制,但并不意味著我們可以隨意存儲,比較大的數(shù)據還是應該考慮持久化存儲。

以上就是源碼解析Android Jetpack組件之ViewModel的使用的詳細內容,更多關于Android Jetpack ViewModel的資料請關注腳本之家其它相關文章!

相關文章

最新評論

封丘县| 额敏县| 潍坊市| 交口县| 诏安县| 浦北县| 报价| 扎赉特旗| 阿拉尔市| 额济纳旗| 东兴市| 汕尾市| 东城区| 沧源| 瑞金市| 黄石市| 东海县| 兴国县| 卓资县| 安平县| 昌宁县| 徐闻县| 马尔康县| 惠来县| 丰都县| 民县| 崇仁县| 宣化县| 吉首市| 改则县| 东乌珠穆沁旗| 锦州市| 陆川县| 静安区| 象州县| 白山市| 新乡市| 松桃| 南木林县| 桃园县| 从化市|