從源碼看 Jetpack(6)- ViewModel 源碼詳解

Google Jetpack 自從推出以后,極大地改變了 Android 開發者們的開發模式,并降低了開發難度。這也要求我們對當中一些子組件的實現原理具有一定的了解,所以我就打算來寫一系列 Jetpack 源碼解析的文章,希望對你有所幫助 ??????

ViewModel 是 Jetpack 整個家族體系內最為基礎的組件之一,基本是按照如下方式來進行初始化和使用的:

  • ViewModelStoreOwner(Activity/Fragment)通過 ViewModelProvider 來得到一個 ViewModel 實例
  • 通過和 LifecycleOwner 綁定的方式來監聽 LiveData 數據的變化從而做出各種響應
  • 當 Activity 由于意外情況被銷毀重建時,Activity 依然能拿到同個 ViewModel 實例,并依靠之前已經保存的數據來進行狀態還原,這也是 ViewModel 最大的特點和優勢
/**
 * @Author: leavesCZY
 * @Github:https://github.com/leavesCZY
 */
class MainActivity : AppCompatActivity() {

    private val myViewModel by lazy {
        ViewModelProvider(this).get(MyViewModel::class.java).apply {
            nameLiveData.observe(this@MainActivity, {

            })
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

}

class MyViewModel : ViewModel() {

    val nameLiveData = MutableLiveData<String>()

    override fun onCleared() {
        super.onCleared()
        Log.e("MyViewModel", "onCleared")
    }

}

下面就來通過提問的方式來拆解 ViewModel 的各個知識點,一步步介紹其實現原理,基于以下版本來進行講解

compileSdkVersion 30
implementation 'androidx.appcompat:appcompat:1.3.0-beta01'
implementation "androidx.lifecycle:lifecycle-viewmodel:2.3.0"

一、如何初始化

在上面的例子中,我們并沒有看到 ViewModel 是如何進行初始化的,也沒有手動調用 ViewModel 的構造函數來創建 ViewModel 實例,這是因為這個操作都隱藏在了 ViewModelProvider 內部,由 ViewModelProvider 自己來通過反射構建出 ViewModel 實例

ViewModelProvider 一共包含三個構造函數,可以看到,不管是哪種方式,最終都是要拿到兩個構造參數:ViewModelStore 和 Factory,且都不能為 null

private final Factory mFactory;

private final ViewModelStore mViewModelStore;

public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
    this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
            ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
            : NewInstanceFactory.getInstance());
}

public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
    this(owner.getViewModelStore(), factory);
}

public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
    mFactory = factory;
    mViewModelStore = store;
}

AppCompatActivity 的父類 androidx.activity.ComponentActivity 已經實現了 ViewModelStoreOwner 和 HasDefaultViewModelProviderFactory 兩個接口,所以我們可以直接使用只包含一個參數的構造函數,而如果傳入的 ViewModelStoreOwner 實例沒有繼承 HasDefaultViewModelProviderFactory 接口的話,mFactory 就使用 NewInstanceFactory 來初始化

Factory 是 ViewModelProvider 的內部接口,用于實現初始化 ViewModel 的邏輯。例如,NewInstanceFactory 就通過反射來實例化 ViewModel 實例,但是也只適用于不包含構造參數的情況,如果是有參構造函數的話就需要我們來主動實現 Factory 接口,畢竟構造參數也需要我們來主動傳入

public interface Factory {
    @NonNull
    <T extends ViewModel> T create(@NonNull Class<T> modelClass);
}

public static class NewInstanceFactory implements Factory {

    private static NewInstanceFactory sInstance;

    @NonNull
    static NewInstanceFactory getInstance() {
        if (sInstance == null) {
            sInstance = new NewInstanceFactory();
        }
        return sInstance;
    }

    @SuppressWarnings("ClassNewInstance")
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        //noinspection TryWithIdenticalCatches
        try {
            //直接通過反射來完成 ViewModel 的初始化
            //傳入的 ViewModelClass 必須包含無參構造函數 
            return modelClass.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        }
    }
}

ComponentActivity 的 getDefaultViewModelProviderFactory() 方法返回的是 SavedStateViewModelFactory,它和 Jetpack 的另外一個組件 SavedStateHandle 有關,在下一篇文章中會介紹,在這里 SavedStateViewModelFactory 起的作用就和 NewInstanceFactory 完全一樣

private ViewModelProvider.Factory mDefaultFactory;

@NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
    if (getApplication() == null) {
        throw new IllegalStateException("Your activity is not yet attached to the "
                + "Application instance. You can't request ViewModel before onCreate call.");
    }
    if (mDefaultFactory == null) {
        mDefaultFactory = new SavedStateViewModelFactory(
                getApplication(),
                this,
                getIntent() != null ? getIntent().getExtras() : null);
    }
    return mDefaultFactory;
}

既然 Factory 實例也有了,下一步就是來調用 ViewModelProvider(this).get() 方法了。get() 方法需要我們傳入 Class 對象,ViewModelProvider 需要拿到 Class 才能完成反射操作。在此方法里主要是通過 modelClass 來自動生成一個字符串 Key,并將參數轉發給另外一個 get() 方法

@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
    String canonicalName = modelClass.getCanonicalName();
    if (canonicalName == null) {
        throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
    }
    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}

可以看出來,以下方法會通過 key 從 ViewModelStore 里取 ViewModel 實例,如果取不到值或者是取出來的值類型不符,則會通過 mFactory.create(modelClass) 方法來反射初始化 ViewModel,并在返回初始化結果前將它存到 mViewModelStore 中,這樣就完成了 ViewModel 的初始化流程了

private final ViewModelStore mViewModelStore;

@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    ViewModel viewModel = mViewModelStore.get(key);
    if (modelClass.isInstance(viewModel)) {
        if (mFactory instanceof OnRequeryFactory) {
            ((OnRequeryFactory) mFactory).onRequery(viewModel);
        }
        return (T) viewModel;
    } else {
        //noinspection StatementWithEmptyBody
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    if (mFactory instanceof KeyedFactory) {
        viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
    } else {
        viewModel = mFactory.create(modelClass);
    }
    mViewModelStore.put(key, viewModel);
    return (T) viewModel;
}

二、如何保持不變

Activity 每次獲取 ViewModel 實例都會先嘗試從 mViewModelStore 中取值,只有在取不到值的時候才會去重新構建一個新的 ViewModel 實例,且構建后的 ViewModel 實例也會被保存在mViewModelStore 中。那既然 Activity 可以在頁面銷毀重建的情況下獲取到之前的 ViewModel 實例,那么不也就間接說明了在這種情況下 ViewModelStore 也是一直被保留著而沒有被回收嗎?

所以,想要知道 ViewModel 是如何保持不變的,那就看 ViewModelStore 實例是如何被保留不被回收就可以了

ViewModelStore 本身實現的邏輯挺簡單的,通過一個 HashMap 來緩存每一個 ViewModel 實例,并提供了存取 ViewModel 的方法

public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }

    Set<String> keys() {
        return new HashSet<>(mMap.keySet());
    }

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

由于 AppCompatActivity 的父類 androidx.activity.ComponentActivity 已經實現了 ViewModelStoreOwner 接口,所以也相當于每個 AppCompatActivity 都持有了一個 ViewModelStore 實例

public interface ViewModelStoreOwner {
    @NonNull
    ViewModelStore getViewModelStore();
}

ComponentActivity 的 getViewModelStore() 方法獲取 ViewModelStore 實例的來源有兩種:

  • 如果 NonConfigurationInstances 不為 null 則通過它獲取。對應 Activity 由于配置更改導致重建的情況,NonConfigurationInstances 當中就保留了頁面重建過程中被保留下來的數據,此時就可以獲取到上一個 Activity 保存的 ViewModelStore 實例了
  • 直接初始化 ViewModelStore 實例返回。對應 Activity 正常被啟動的情況

這里只要看第一種情況即可

private ViewModelStore mViewModelStore;

@NonNull
@Override
public ViewModelStore getViewModelStore() {
    if (getApplication() == null) {
        throw new IllegalStateException("Your activity is not yet attached to the "
                + "Application instance. You can't request ViewModel before onCreate call.");
    }
    ensureViewModelStore();
    return mViewModelStore;
}

@SuppressWarnings("WeakerAccess") /* synthetic access */
void ensureViewModelStore() {
    if (mViewModelStore == null) {
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
}

NonConfigurationInstances 是 ComponentActivity 的一個靜態內部類,其內部就包含了一個 ViewModelStore 成員變量,在 Activity 被重建時,其對應的 ViewModelStore 就被保存在了這里

static final class NonConfigurationInstances {
    Object custom;
    ViewModelStore viewModelStore;
}

通過查找引用,可以找到 ComponentActivity 就是在 onRetainNonConfigurationInstance() 方法里來完成 NonConfigurationInstances.viewModelStore 變量的賦值。從該方法名可以猜出,該方法就用于獲取非配置項實例,以便在后續重建 Activity 時恢復數據

@Override
@Nullable
@SuppressWarnings("deprecation")
public final Object onRetainNonConfigurationInstance() {
    Object custom = onRetainCustomNonConfigurationInstance();
    ViewModelStore viewModelStore = mViewModelStore;
    if (viewModelStore == null) {
        //如果 Activity 在第一次被重建后還未調用過 getViewModelStore() 方法,此時 mViewModelStore 就還是為 null
        //之后又發生了第二次重建,那就主動調用 getLastNonConfigurationInstance() 來獲取第一次重建時保存的 ViewModelStore 實例
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            viewModelStore = nc.viewModelStore;
        }
    }
    if (viewModelStore == null && custom == null) {
        return null;
    }
    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    //將 viewModelStore 打包帶走
    nci.viewModelStore = viewModelStore;
    return nci;
}

通過查找方法引用,可以知道 onRetainNonConfigurationInstance() 又是被父類 android.app.Activity 的以下方法所調用,由父類去負責保留 NonConfigurationInstances 對象

NonConfigurationInstances retainNonConfigurationInstances() {
    //拿到子類需要保存的數據
    Object activity = onRetainNonConfigurationInstance();

    HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
    FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

    // We're already stopped but we've been asked to retain.
    // Our fragments are taken care of but we need to mark the loaders for retention.
    // In order to do this correctly we need to restart the loaders first before
    // handing them off to the next activity.
    mFragments.doLoaderStart();
    mFragments.doLoaderStop(true);
    ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();

    if (activity == null && children == null && fragments == null && loaders == null
            && mVoiceInteractor == null) {
        return null;
    }

    NonConfigurationInstances nci = new NonConfigurationInstances();
    //保存起來
    nci.activity = activity;
    nci.children = children;
    nci.fragments = fragments;
    nci.loaders = loaders;
    if (mVoiceInteractor != null) {
        mVoiceInteractor.retainInstance();
        nci.voiceInteractor = mVoiceInteractor;
    }
    return nci;
}

從以上流程可以看出 Activity 的一些設計思路。由于 android.app.Activity 的邏輯是和特定的系統版本 SDK 關聯的,我們無法決定用戶手中的手機系統版本。而我們日常開發中都是選擇直接繼承于androidx.appcompat.app.AppCompatActivity,它又是作為一個依賴庫來存在的,開發者可以自行決定要使用哪個版本號,Google 官方也可能隨時推出新版本。所以,android.app.Activity 就將非配置項實例數據均當做一個 Object 實例來處理,由子類通過實現onRetainNonConfigurationInstance() 方法來返回,父類 Activity 不限制方法返回值需要特定類型,不同的子類可以返回不同的類型,父類只負責在需要的時候將實例保存起來,然后在重建時返回給子類即可,由子類自己來進行數據的拆解和重建。這樣,不管用戶使用的手機是哪個系統版本,都可以保證三方依賴庫有最大的發揮余地

再來看下 retainNonConfigurationInstances() 方法是在哪里調用的

通過搜索,可以找到在 ActivityThread 類的以下方法存在調用,該方法用于回調 Activity 的 onDestroy 方法,在回調前會先將數據保存到 ActivityClientRecord 的 lastNonConfigurationInstances 字段中

/** Core implementation of activity destroy call. */
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
        int configChanges, boolean getNonConfigInstance, String reason) {
    ActivityClientRecord r = mActivities.get(token);
    ···
        if (getNonConfigInstance) {
            try {
                //保存 Activity 返回的 NonConfigurationInstances
                r.lastNonConfigurationInstances
                        = r.activity.retainNonConfigurationInstances();
            } catch (Exception e) {
                if (!mInstrumentation.onException(r.activity, e)) {
                    throw new RuntimeException(
                            "Unable to retain activity "
                            + r.intent.getComponent().toShortString()
                            + ": " + e.toString(), e);
                }
            }
        }

    ···
    //調用 Activity 的 onDestroy 方法
    mInstrumentation.callActivityOnDestroy(r.activity);    
    ···
    return r;
}

在重新啟動 Activity 時,又會將數據 attach 到新的 Activity 實例上,將其作為 getLastNonConfigurationInstance() 方法的返回值。通過這種數據交接,重建前的 ViewModelStore 實例就會被重建后的 Activity 拿到,當中就保留了重建前 Activity 初始化的所有 ViewModel 實例,從而保障了 ViewModel 實例的不變性

/**  Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ···
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        r.intent.prepareToEnterProcess();
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                "Unable to instantiate activity " + component
                + ": " + e.toString(), e);
        }
    }

    ···

    //將 r.lastNonConfigurationInstances 傳遞進去
    activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor, window, r.configCallback,
                    r.assistToken);
    ···
    return activity;
}

三、如何調用構造函數

ViewModelProvider 提供的 Factory 接口實現類有兩個:

  • NewInstanceFactory。通過反射來實例化 ViewModel,適用于包含無參構造函數的情況
  • AndroidViewModelFactory。通過反射來實例化 ViewModel,適用于構造參數只有一個,且參數類型為 Application 的情況

如果想要通過其它類型的構造函數來初始化 ViewModel 的話,就需要我們自己來實現 ViewModelProvider.Factory 接口聲明初始化邏輯了,就像以下這樣

/**
 * @Author: leavesCZY
 * @Github:https://github.com/leavesCZY
 */
class MainActivity : AppCompatActivity() {

    private val myViewModelA by lazy {
        ViewModelProvider(this, object : ViewModelProvider.Factory {
            override fun <T : ViewModel> create(modelClass: Class<T>): T {
                return MyViewModel(10) as T
            }
        }).get(
            MyViewModel::class.java
        ).apply {
            nameLiveData.observe(this@MainActivity, {

            })
        }
    }

    private val myViewModelB by lazy {
        ViewModelProvider(this, object : ViewModelProvider.Factory {
            override fun <T : ViewModel> create(modelClass: Class<T>): T {
                return MyViewModel(20) as T
            }
        }).get(
            MyViewModel::class.java
        ).apply {
            nameLiveData.observe(this@MainActivity, {

            })
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.e("myViewModelA", myViewModelA.toString() + " age: " + myViewModelA.age)
        Log.e("myViewModelB", myViewModelB.toString() + " age: " + myViewModelB.age)
    }

}

class MyViewModel(val age: Int) : ViewModel() {

    val nameLiveData = MutableLiveData<String>()

}

需要注意的是,雖然 myViewModelAmyViewModelB 都有各自不同的入參參數,但從日志輸出結果來看它們其實是同一個對象,即最先初始化的那個 ViewModel 實例會被緩存下來重復使用

 E/myViewModelA: github.leavesc.demo.MyViewModel@e24ac80 age: 10
 E/myViewModelB: github.leavesc.demo.MyViewModel@e24ac80 age: 10

之所以會出現以上情況,是因為在初始化 myViewModelAmyViewModelB 的時候它們默認對應的都是同個 Key,ViewModelProvider 默認情況下是以 DEFAULT_KEY + ":" + canonicalName 作為 key 值來從 mViewModelStore 中取值,所以在初始化 myViewModelB 的時候就直接把之前已經初始化好的 myViewModelA 給返回了

@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
    String canonicalName = modelClass.getCanonicalName();
    if (canonicalName == null) {
        throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
    }
    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}

@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    ViewModel viewModel = mViewModelStore.get(key);

    if (modelClass.isInstance(viewModel)) {
        if (mFactory instanceof OnRequeryFactory) {
            ((OnRequeryFactory) mFactory).onRequery(viewModel);
        }
        //如果 mViewModelStore 里已經緩存了同個 key,且 value 也對應相同的 Class 類型,那么就直接返回 value 
        return (T) viewModel;
    } else {
        //noinspection StatementWithEmptyBody
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    if (mFactory instanceof KeyedFactory) {
        viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
    } else {
        viewModel = mFactory.create(modelClass);
    }
    mViewModelStore.put(key, viewModel);
    return (T) viewModel;
}

如果希望 myViewModelAmyViewModelB 對應不同的實例對象,那么就需要在初始化的時候主動為它們指定不同的 Key,這樣它們就可以一起被存到 ViewModelStore 的 HashMap 中了

private val myViewModelA by lazy {
    ViewModelProvider(this, object : ViewModelProvider.Factory {
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return MyViewModel(10) as T
        }
    }).get(
        "keyA", MyViewModel::class.java
    ).apply {
        nameLiveData.observe(this@MainActivity, {

        })
    }
}

private val myViewModelB by lazy {
    ViewModelProvider(this, object : ViewModelProvider.Factory {
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return MyViewModel(20) as T
        }
    }).get(
        "keyB", MyViewModel::class.java
    ).apply {
        nameLiveData.observe(this@MainActivity, {

        })
    }
}
E/myViewModelA: github.leavesc.demo.MyViewModel@e24ac80 age: 10
E/myViewModelB: github.leavesc.demo.MyViewModel@9abd6fe age: 20

四、什么時候回收

要知道 ViewModel 是在何時回收的,那么就只要看 ViewModelStore 是在什么時候清空 HashMap 就可以了

通過查找方法引用,可以發現是在 ComponentActivity 中調用了 ViewModelStore 的 clear() 方法。Activity 在收到 ON_DESTROY 事件時,如果判斷到是由于配置項更改導致了 Activity 被銷毀,那么就不會調用 getViewModelStore().clear() 。如果是正常退出 Activity,那就會調用 getViewModelStore().clear() 方法,這樣就會清空掉所有緩存的 ViewModel 實例了,ViewModel 的 clear() 方法也同時會被調用

public ComponentActivity() {
    ···
    getLifecycle().addObserver(new LifecycleEventObserver() {
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
            if (event == Lifecycle.Event.ON_DESTROY) {
                // Clear out the available context
                mContextAwareHelper.clearAvailableContext();
                // And clear the ViewModelStore
                if (!isChangingConfigurations()) {
                    getViewModelStore().clear();
                }
            }
        }
    });
    ···
}
public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    ···
        
    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

五、初始化陷阱

看以下代碼,觀察當應用啟動時日志的輸出結果

/**
 * @Author: leavesCZY
 * @Github:https://github.com/leavesCZY
 */
class MainActivity : AppCompatActivity() {

    private val aViewModel by lazy {
        ViewModelProvider(this).get(
            "myKey", AViewModel::class.java
        )
    }

    private val bViewModel by lazy {
        ViewModelProvider(this).get(
            "myKey", BViewMode::class.java
        )
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.e("aViewModel", aViewModel.toString())
        Log.e("bViewModel", bViewModel.toString())
        Log.e("MainActivity", "onCreate")
    }

}

class AViewModel() : ViewModel() {

    override fun onCleared() {
        super.onCleared()
        Log.e("AViewModel", "onCleared")
    }

}

class BViewMode : ViewModel() {

    override fun onCleared() {
        super.onCleared()
        Log.e("BViewMode", "onCleared")
    }

}

日志的輸出會比較反直覺:aViewModel 在剛被初始化不久就被回收了,而此時 MainActivity 才剛執行到 onCreate 方法

E/aViewModel: github.leavesc.demo.AViewModel@3c93503
E/AViewModel: onCleared
E/bViewModel: github.leavesc.demo.BViewMode@e24ac80
E/MainActivity: onCreate

之所以造成這一結果,就是因為 aViewModelbViewModel 都使用了同個 key,這就導致了在將 bViewModel 存到 HashMap 的時候就會覆蓋并回收掉舊值 aViewModel

public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            //存在舊值的話就將其回收
            oldViewModel.onCleared();
        }
    }

}

所以,對于不同類型的 ViewModel,在初始化的時候不能指定相同的 Key

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。