从源码看 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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342