ViewModel原理

概述

ViewModel是Android Jetpack组件库中的一个组件,在Android MVVM模式中被广泛使用。

ViewModel的主要特性是以注重生命周期的方式存储和管理数据。

这种特性体现在ViewModel与Android组件(Activity、Fragment和Service)的生命周期相绑定,在组件的生命周期中ViewModel的数据会一直保存在内存中,即便是在系统配置变更时也会存在。

image.png

ViewModel的优势

与UI层低耦合
在MVVM模式中,数据和业务逻辑可以集中在ViewModel中处理,实现功能时只需要在ViewModel中关注数据和业务即可,不需要和UI或者控件打交道。

Fragment间共享ViewModel
与其说是共享ViewModel,不如说是共享数据。在Android文档中有提到通过ViewModel在Fragment中共享数据,使用ViewModel共享数据可以免去Fragment之前相互回调。

数据一直保存在内存中
这个优势具体来讲是在系统配置发生变化导致Activity重建时,ViewModel还是会保持在内存中。在一些场景中可以通过这种方式避免重复加载数据。

ViewModel工作原理

在分析ViewModel的工作原理之前,我们最好带着问题去分析。

  • ViewModel如何被创建的,在什么时候被销毁;
  • ViewModel的生命周期如何与组件生命周期绑定;
  • 为什么Activity重建时ViewModel的数据没有丢失;
  • Fragment之前如何共享ViewModel;

ViewModel的创建与销毁

ViewModel的创建
分析ViewModel的创建自然是从ViewModel初始化的地方开始。

viewModel = ViewModelProvider(this).get(TestViewModel::class.java)

ViewModel的初始化很简单,通过ViewModelProvider就可以获取到ViewModel实例。那么从这里入手开始分析ViewModel的创建过程。

第一步是先初始化了一个ViewModelProvider对象,所以我们先看下ViewModelProvider的构造方法;

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

从ViewModelProvider的构造方法中可以看到最终是需要两个参数ViewModelStoreOwner以及Factory。这两个参数中ViewModelStoreOwner是用来存储ViewModel对象的,Factory是用来创建ViewModel对象。这个接下来的分析中也会提到。

第二步是通过ViewModelProvider的get()方法获取ViewModel实例。

public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key); // 1.是否有缓存ViewModel实例缓存

        ...
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

在get()方法中可以看到是通过mFactory的类型来创建ViewModel的。而Factory的类型是由ViewModelStoreOwner决定的,这是ViewModelProvider的构造方法中的逻辑。其中有两种Factory,一种是SavedStateViewModelFactory,另一种是NewInstanceFactory。接下来我们直接看两种方式的区别。

SavedStateViewModelFactory创建ViewModel

public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
        boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
        Constructor<T> constructor;
        ...
        SavedStateHandleController controller = SavedStateHandleController.create(
                mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
        try {
            T viewmodel;
            if (isAndroidViewModel) {
                viewmodel = constructor.newInstance(mApplication, controller.getHandle());
            } else {
                viewmodel = constructor.newInstance(controller.getHandle());
            }
            viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
            return viewmodel;
        } 
        ...
    }

在代码中可以看到,ViewModel有分为AndroidViewModel跟ViewModel,它们的区别是AndroidViewModel创建时会加入Application参数。

NewInstanceFactory创建ViewModel

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

NewInstanceFactory创建过程相对简单,通过Class的newInstance()方法直接创建ViewModel实例。

ViewModel的销毁

这里还要分开两处说,一个是在Activity中的销毁过程,一个是在Fragment中销毁过程。

首先我们把目光放到ViewModel类来看下ViewModel销毁时都做了什么。

protected void onCleared() {}

final void clear() {
    mCleared = true;
    if (mBagOfTags != null) {
        synchronized (mBagOfTags) {
            for (Object value : mBagOfTags.values()) {
                // see comment for the similar call in setTagIfAbsent
                closeWithRuntimeException(value);
            }
        }
    }
    onCleared();
}

可以看到ViewModel中有clear()方法和onCleared()方法。通过跟踪方法的调用可以知道ViewModel的销毁过程。最终在ViewModelStore类中找到了clear()方法。

public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }

Activity中的销毁
继续跟踪代码可以看到在ComponentActivity中:

 public ComponentActivity() {
    getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    if (!isChangingConfigurations()) {
                        // 销毁ViewModel
                        getViewModelStore().clear();
                    }
                }
            }
        });
 }

在ComponentActivity的构造方法中,可以看到通过Lifecycle在ON_DESTROY事件中销毁ViewModel。

Fragment中的销毁
首先通过代码跟踪到ViewModelStore的clear()方法调用的地方,在FragmentManagerViewModel类的clearNonConfigState()方法中找到了ViewModel的销毁逻辑。

void clearNonConfigState(@NonNull Fragment f) {
    ...
    // Clear and remove the Fragment's ViewModelStore
    ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
    if (viewModelStore != null) {
        // 销毁ViewModel
        viewModelStore.clear();
        mViewModelStores.remove(f.mWho);
    }
}

如果继续跟踪代码可以看到代码的调用栈是 FragmentStateManager::destroy() -> (Fragment状态切换)->FragmentManager::dispatchDestroy()->FragmentActivity::onDestory()。

ViewModel的生命周期绑定
在上面分了ViewModel的创建与销毁过程中就可以得出ViewModel生命周期是如何与组件交互的了。主要还是通过Lifecycle和组件的生命周期方法来进行回调管理。

Activity重建下的ViewModel

按照上面的逻辑,在Activity重建时会执行destory生命周期事件,那么为什么ViewModel没有销毁呢?

还是直接在代码里找答案,通过对ComponentActivity的getViewModelStore()方法进行分析。可以找到这个问题的答案。

public ViewModelStore getViewModelStore() {
        ...
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

可以看大mViewModelStore变量如果是null的话,会从NonConfigurationInstances实例中取。那么我们就分析NonConfigurationInstances实例的来源。

从NonConfigurationInstances的名称可以大致判断这个类与配置无关。

public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();

        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            // No one called getViewModelStore(), so see if there was an existing
            // ViewModelStore from our last NonConfigurationInstance
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                viewModelStore = nc.viewModelStore;
            }
        }

        if (viewModelStore == null && custom == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }

继续分析下去,可以看到在onRetainNonConfigurationInstance中会存储ViewModelStore实例,这也就是为什么ViewModel不会在Activity重建时被销毁的原因。

Fragment之间共享ViewModel

在分析ViewModel的销毁过程时,我们看到Activity与Fragment存储VieModel是分离的,那么同一个Activity下的Fragment是如何共享ViewModel的呢?
这里先说答案,它们其实共享的是Activity的ViewModel。

 private val model: SharedViewModel by activityViewModels()

这个实现的逻辑就在activityViewModels()中。

inline fun <reified VM : ViewModel> Fragment.activityViewModels(
    noinline factoryProducer: (() -> Factory)? = null
) = createViewModelLazy(VM::class, { requireActivity().viewModelStore },
    factoryProducer ?: { requireActivity().defaultViewModelProviderFactory })

在activityViewModels()的实现中可以看到是requireActivity()获取的viewModelStore。以此来实现共享ViewModel。

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

推荐阅读更多精彩内容