Jetpack-ViewModel源码解析

官方文档:https://developer.android.com/topic/libraries/architecture/viewmodel

前言

ViewModel的源码比较简单,代码也不是很多,网上已经有了很多的文章来讲 ViewModel,但是最近查看源码时发现源码中状态保存的实现逻辑有了较大的变化,所以还是记录下来,并重新梳理了一遍源码的分析过程。

PS:本文是基于 android.arch.lifecycle:viewmodel:1.1.1 的源码进行分析

简单示例

class CountLiveData : LiveData<Int>() {
    private var count = 0

    fun doCount() {
        value = ++count
    }
}
class MyViewModel : ViewModel() {
    var data = CountLiveData()

    fun doCount() {
        data.doCount()
    }
}
class ViewModelDemoActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_demo_view_model)
        // 获取 ViewModel 对象
        val viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
        // 监听数据源
        viewModel.data.observe(this, Observer { tv_test.text = it.toString() })
        // 点击按钮改变数据
        btn_test.setOnClickListener {
            viewModel.doCount()
        }
    }
}

源码解析

核心类

  • ViewModelProviders
  • ViewModelProvider
  • ViewModelStore
  • FragmentActivity

一、ViewModel的创建

ViewModel的实例化很简单,代码如下

val viewModel = ViewModelProviders.of(activity).get(MyViewModel::class.java)

1) ViewModelProviders.of(activity, factory) 方法

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) {
    Application application = checkApplication(activity);
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    return new ViewModelProvider(activity.getViewModelStore(), factory);
}

参数也可以是Fragment,这里以Activity为例,先来看of方法的第二个参数 Factory

public interface Factory {
    /**
     * Creates a new instance of the given {@code Class}.
     * <p>
     *
     * @param modelClass a {@code Class} whose instance is requested
     * @param <T>        The type parameter for the ViewModel.
     * @return a newly created ViewModel
     */
    @NonNull
    <T extends ViewModel> T create(@NonNull Class<T> modelClass);
}

Factory 是 ViewModelProvider 的内部类,定义了一个 create 方法,作用就是创建一个 ViewModel 对象,这个很好理解。Factory 接口默认有两个实现类,分别是 NewInstanceFactory 和 AndroidViewModelFactory,其中 NewInstanceFactory 的实现就是通过反射调用构造器创建 ViewModel 对象,AndroidViewModelFactory 则是 NewInstanceFactory 的子类,在 NewInstanceFactory 的基础上多一个 Context 字段,以下是两个类的源码。

  • NewInstanceFactory 源码
public static class NewInstanceFactory implements Factory {

    @SuppressWarnings("ClassNewInstance")
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        //noinspection TryWithIdenticalCatches
        try {
            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);
        }
    }
}
  • AndroidViewModelFactory 源码
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
    
    private static AndroidViewModelFactory sInstance;

    /**
     * Retrieve a singleton instance of AndroidViewModelFactory.
     *
     * @param application an application to pass in {@link AndroidViewModel}
     * @return A valid {@link AndroidViewModelFactory}
     */
    @NonNull
    public static AndroidViewModelFactory getInstance(@NonNull Application application) {
        if (sInstance == null) {
            sInstance = new AndroidViewModelFactory(application);
        }
        return sInstance;
    }

    private Application mApplication;

    /**
     * Creates a {@code AndroidViewModelFactory}
     *
     * @param application an application to pass in {@link AndroidViewModel}
     */
    public AndroidViewModelFactory(@NonNull Application application) {
        mApplication = application;
    }

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
            //noinspection TryWithIdenticalCatches
            try {
                return modelClass.getConstructor(Application.class).newInstance(mApplication);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
        return super.create(modelClass);
    }
}

我们继续回来看 ViewModelProviders.of(activity, factory) 方法,不用往上翻了,我再贴一下源码:

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) {
    Application application = checkApplication(activity);
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    return new ViewModelProvider(activity.getViewModelStore(), factory);
}

这里 factory 如果为 null,则会创建一个默认的 Factory 对象,类型就是上面讲到的 AndroidViewModelFactory。最后,of 方法返回了一个通过构造方法创建的 ViewModelProvider 对象,第一个参数是 activity.getViewModelStore(),这里的activity 是 FragmentActivity 类型

public class FragmentActivity extends ComponentActivity implements
        ViewModelStoreOwner,
        ActivityCompat.OnRequestPermissionsResultCallback,
        ActivityCompat.RequestPermissionsRequestCodeValidator {
    @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.");
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
        return mViewModelStore;
    }
}

在 FragmentActivity 的源码中可以看到,FragmentActivity 实现了
ViewModelStoreOwner 接口,接口中定义了 getViewModelStore() 方法,FragmentActivity 实现 getViewModelStore() 方法并返回了一个 ViewModelStore 对象。

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

在 ViewModelStore 中,定义了一个 HashMap 对象,存储的 key 是包含各 ViewModel 子类完整类名的字符串(在后面的源码中会看到),value 则是 ViewModel 对象。所以 ViewModelStore 其实就是一个容器,内部保存了所有的 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);
    }

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

2) ViewModelProvider 类

ViewModelProviders.of() 方法最后创建并返回了一个 ViewModelProvider 对象,那我们就继续来看 ViewModelProvider 类,首先来看它的构造方法:

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

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

在构造方法中保存了 ViewModelStore 和 Factory 对象,这两个参数在之前都已经介绍过了,ViewModelStore 用于保存 ViewModel 对象,Factory 用于创建 ViewModel 对象。

到这里,一个 ViewModelProvider 对象就创建好了,然后我们会调用 VideModelProvider 的 get 方法来获取 ViewModel 对象,看源码:

public class ViewModelProvider {

    private static final String DEFAULT_KEY = "android.arch.lifecycle.ViewModelProvider.DefaultKey";

    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull Class<T>     modelClass) {
        // canonicalName 是 modelClass 的完整类名(例如:     com.evan.demo.MyViewModel)
        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)) {
            //noinspection unchecked
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
    
        viewModel = mFactory.create(modelClass);
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    } 
}

get 方法传入的参数应是 ViewModel 子类的 Class 对象,然后在 mViewModelStore 查看是否有缓存,如果有则直接返回,如果没有则通过 mFactory 创建一个并存入 mViewModelStore。

到这里为止,我们已经读完了获取 ViewModel 对象的源码。

小结:
  1. 通过 ViewModelProviders.of(activity, factory) 获取 ViewModelProvider 实例,ViewModelProvider 中包含一个 ViewModelStore 实例,用于保存已创建的 ViewModel 实例(ViewModelStore 通过 FragmentActivity 的 getViewModelStore() 方法获取),factory则是用于创建ViewModel实例
  2. 通过 ViewModelProvider.get(modelClass) 方法可以获取 ViewModel实例,实际就是从 ViewModelStore 容器中取出对应的 ViewModel实力缓存,如果没有则通过 factory 创建实例并存入 ViewModelStore

二、ViewModel 实现数据持久化原理

还记得么?之前有提到过,ViewModelStore 的实例是由 FragmentActivity 中的 getViewModelStore() 方法提供的,我们再来看源码:

public class FragmentActivity extends ComponentActivity implements
        ViewModelStoreOwner,
        ActivityCompat.OnRequestPermissionsResultCallback,
        ActivityCompat.RequestPermissionsRequestCodeValidator {
    @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.");
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
        return mViewModelStore;
    }
}

从代码中可以看到,FragmentActivity中有一个 mViewModelStore 字段,保存了 ViewModelStore 实例,我们可以在 FragmentActivity 的源码中对搜索 mViewModelStore,就可以找到几处对 mViewModelStore 的逻辑处理,这些代码的位置就是 ViewModel 能够做到状态保存的核心代码,我们一起来看一下。

/**
 * Retain all appropriate fragment state.  You can NOT
 * override this yourself!  Use {@link #onRetainCustomNonConfigurationInstance()}
 * if you want to retain your own state.
 */
@Override
public final Object onRetainNonConfigurationInstance() {
    // 开发者自定义的状态保存
    Object custom = onRetainCustomNonConfigurationInstance();

    FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

    if (fragments == null && mViewModelStore == null && custom == null) {
        return null;
    }

    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = mViewModelStore;
    nci.fragments = fragments;
    // 返回的 NonConfigurationInstances 对象不会被回收
    return nci;
}

上面这段代码是最核心的部分,大家都知道 Activity 在横竖屏切换时会回调 onSaveInstanceState 方法用于保存数据,之后再通过 onCreate 或 onRestoreInstanceState 方法还原数据。但是,可能很多人不知道的是,在 Activity 中还有两个方法也同样可以用于保存Activity的状态,它们是 onRetainNonConfigurationInstance 和 getLastNonConfigurationInstance 方法。Activity 在重建时会回调 onRetainNonConfigurationInstance 方法,该方法会返回一个Object对象保存状态,而不再是Bundle类型了。在 Activity 恢复时可以调用 getLastNonConfigurationInstance() 方法获取最近一次 onRetainNonConfigurationInstance() 方法返回的 Object 对象用于恢复状态。

我们可以看到在源码中 onRetainNonConfigurationInstance 方法返回的是一个 NonConfigurationInstances 对象,NonConfigurationInstances 类很简单,只保存了三个对象,分别是custon(自定义状态)、viewModelStore、fragments(Activity中的所有Fragment的保存状态),既然 viewModelStore 跟着 NonConfigurationInstances 实例一起在 onRetainNonConfigurationInstance 方法中返回了,viewModelStore 自然也就被保留了下来,不会被在横竖屏切换时回收。

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

那既然 viewModelStore 保存了下来,那肯定也会有恢复状态的逻辑,请看以下源码:

/**
 * Perform initialization of all fragments.
 */
@SuppressWarnings("deprecation")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    mFragments.attachHost(null /*parent*/);

    super.onCreate(savedInstanceState);

    NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
    if (nc != null) {
        mViewModelStore = nc.viewModelStore;
    }
    // ...
}

可以看到,在 FragmentActivity 的 onCreate 中调用 getLastNonConfigurationInstance 方法做了相应的处理

PS:细心的同学应该也注意到了 onRetainNonConfigurationInstance 方法源码中的注释,官方不允许开发者直接覆写onRetainNonConfigurationInstance()方法,应该该方法中已经了默认实现,其中就包括 ViewModelStore 实例的保存,如要开发者需要保存恢复其他数据,则应该覆写 onRetainCustomNonConfigurationInstance方法。

三、ViewModel 的资源回收

根据官方文档描述 ViewModel的生命周期范围涵盖了整个Activity或Fragment的生命周期,包括Activity重建触发的生命周期(比如屏幕旋转或配置更改等情况),当生命周期结束时,会回调onCleared方法,如下图所示:


viewmodel-lifecycle.png

FragmentActivity 在 onDestroy 方法中将 mViewModelStore 资源回收了

/**
 * Destroy all fragments.
 */
@Override
protected void onDestroy() {
    super.onDestroy();

    if (mViewModelStore != null && !isChangingConfigurations()) {
        mViewModelStore.clear();
    }

    mFragments.dispatchDestroy();
}

最后看 ViewModelStore 的源码,在 clear 方法中,调用了所有 ViewModel 实例的 onCleared() 方法

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);
    }

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

至此,ViewModel 源码解析就结束了。

总结

ViewModel 的代码并不复杂,其核心代码其实就是通过 onRetainNonConfigurationInstance 方法进行了状态保存。

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

推荐阅读更多精彩内容