Android Jetpack库剖析之ViewModel组件篇

前言

今天让我们一起去探究一下ViewModel的实现原理,描述的不对或不足还请海涵,仅作为参考

ViewModel简介

ViewModel是一个可感知Activity或Fragment生命周期的一个架构组件,当视图销毁,数据也会被清除,所以它的本质就是用来存储与视图相关的数据,让视图显示控制与数据分离,即使界面配置发生改变数据也不会被销毁,通常配合LiveData使用

ViewModel用法

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(LayoutInflater.from(baseContext))
        setContentView(binding.root)
        //获取ViewModel实例
        val viewModel: TextViewModel = ViewModelProvider(this).get(TextViewModel::class.java)
        //订阅数据
        viewModel.liveData.observe(this, { println(it) })
        //调用函数
        viewModel.test()
    }
    class TextViewModel : ViewModel() {
        val liveData = MediatorLiveData<String>()
        fun test() {
            liveData.postValue("Hello")
        }
    }
}

1,使用ViewModelProvider获取ViewModel实例

2,订阅VIewModel的LiveData

3,调用ViewModel的方法

构造ViewModelProvider过程做了什么

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    

1,当我们创建ViewModelProvider的时候需要传入一个ViewModelStoreOwner对象,ViewModelStoreOwner是负责提供ViewModelStore对象的, 而ComponentActivity实现了这个接口,所以我们默认传当前的Activity即可

2,首先判断是否有默认的ViewModel工厂,如果没有就创建一个Factory

3,Factory是用来创建ViewModel的,ViewModelStore是用来存储ViewModel的

调用get()方法是如何构建ViewModel

    @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) {
        //通过key到ViewModelStore中取
        ViewModel viewModel = mViewModelStore.get(key);

        //如果取到ViewModel 代表已经创建过这个ViewModel 直接返回
        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.
            }
        }
        //通过Factor利用反射创建一个实例
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
        } else {
            viewModel = mFactory.create(modelClass);
        }
        //把ViewModel实例存储到ViewModelStore中
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

1,当我们调用get()方法时会先获取类名,然后生成一个唯一的key

2,先通过key到ViewModelStore中取ViewModel,如果不为空代表已经创建过这个ViewModel的实例,直接返回这个实例

3,如果为空就通过工厂利用java反射机制创建一个实例,通过键值对形式保存到ViewModelStore中,返回实例,看到这里是不是对为什么多个fragment可以共享同一个ViewModel的疑问豁然开朗了,因为Fragment是依附于Activity之上的,在我们构建ViewModelProvider的时候传入同一个Activity,那么ViewModelProvider得到的ViewModelStore是同一个,在我们调用get()方法时就能通过key到ViewModelStore中取到同一个ViewModel实例,说白了就是共用Activity的ViewModel

Activity配置发生改变如何缓存ViewModelStore

ActivityThread{
    private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
            PendingTransactionActions pendingActions, boolean startsNotResumed,
            Configuration overrideConfig, String reason) {
        // Preserve last used intent, it may be set from Activity#setIntent().
        final Intent customIntent = r.activity.mIntent;
        // Need to ensure state is saved.
        if (!r.paused) {
            performPauseActivity(r, false, reason, null /* pendingActions */);
        }
        if (!r.stopped) {
            callActivityOnStop(r, true /* saveState */, reason);
        }
        //销毁Activity
        handleDestroyActivity(r.token, false, configChanges, true, reason);
        //启动Activity
        handleLaunchActivity(r, pendingActions, customIntent);
    }
}

1,当我们切换横竖屏的时候,ActivityThread会接收到RELAUNCH_ACTIVITY消息,会调用自身的handleRelaunchActivityInner(),这个方法里面有一个参数r,类型是ActivityClientRecord,我们每打开一个Activity,ActivityThread就会生成这么个对象来记录我们打开的Activity并保存起来

2,handleRelaunchActivityInner()这个方法里调用了handleDestroyActivity()方法去销毁我们的Activity

ActivityThread{
        @Override
    public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
            boolean getNonConfigInstance, String reason) {
        //执行销毁Activity
        ActivityClientRecord r = performDestroyActivity(token, finishing,
                configChanges, getNonConfigInstance, reason);
    }
    ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance, String reason) {
        //通过token获取Activity的记录
        ActivityClientRecord r = mActivities.get(token);
        if (r != null) {
            //是否需要获取配置实例
            if (getNonConfigInstance) {
                try {
                    //调用Activity的retainNonConfigurationInstances方法获取配置实例
                    r.lastNonConfigurationInstances
                            = r.activity.retainNonConfigurationInstances();
                } catch (Exception e) {
                }
            }
            r.setState(ON_DESTROY);
        }
        //通过token移除这条Activity的记录
        synchronized (mResourcesManager) {
            mActivities.remove(token);
        }
        return r;
    }
}

3,handleDestroyActivity()则直接调用了performDestroyActivity()方法去销毁Activity,核心部分就是调用了Activity的retainNonConfigurationInstances()方法获取了配置实例并复制给了ActivityClientRecord,把NonConfigurationInstances实例保存起来

Activity{
    NonConfigurationInstances retainNonConfigurationInstances() {
        //获取NonConfigurationInstances对象
        Object activity = onRetainNonConfigurationInstance();
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
        mFragments.doLoaderStart();
        mFragments.doLoaderStop(true);
        ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
        //创建NonConfigurationInstances实例
        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;
    }
}
ComponentActivity{
    @Override
    @Nullable
    public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();
        //获取ViewModelStore
        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实例
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }
}

4, 通过查阅源码,一层一层的往下剖析,ActivityThread是通过这样的调用链来获取我们的ViewModelStore实例并保存在ActivityClientRecord中的

Activity重建后如何恢复ViewModelStore

ActivityThread{
    private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
            PendingTransactionActions pendingActions, boolean startsNotResumed,
            Configuration overrideConfig, String reason) {
        // Preserve last used intent, it may be set from Activity#setIntent().
        final Intent customIntent = r.activity.mIntent;
        // Need to ensure state is saved.
        if (!r.paused) {
            performPauseActivity(r, false, reason, null /* pendingActions */);
        }
        if (!r.stopped) {
            callActivityOnStop(r, true /* saveState */, reason);
        }
        //销毁Activity
        handleDestroyActivity(r.token, false, configChanges, true, reason);
        //启动Activity
        handleLaunchActivity(r, pendingActions, customIntent);
    }
}

1,当handleDestroyActivity执行完毕就已经把ViewModelStore的实例获取到并存放到ActivityClientRecord中,此时就开始执行handleLaunchActivity()方法来启动activity

2,handleLaunchActivity()这个方法也需要ActivityClientRecord这个参数,而此时ActivityClientRecord这个对象正是经过handleDestroyActivity()这个方法获取并保存了ViewModelStore 实例的对象

3,handleLaunchActivity()则调用了performLaunchActivity()方法来启动Activity

ActivityThread{
    @Override
    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
        // Make sure we are running with the most recent config.
        handleConfigurationChanged(null, null);
        //去启动Activity
        final Activity a = performLaunchActivity(r, customIntent);
        return a;
    }
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            if (activity != null) {
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (r.overrideConfig != null) {
                    config.updateFrom(r.overrideConfig);
                }
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                Window window = null;
                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }
                appContext.setOuterContext(activity);
                //调用当前打开的Activity的attach()方法
                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);
                r.activity = activity;
            }
            r.setState(ON_CREATE);
            //保存这条Activity记录
            synchronized (mResourcesManager) {
                mActivities.put(r.token, r);
            }
        } catch (SuperNotCalledException e) {
            throw e;
        } catch (Exception e) {
        }
        return activity;
    }
}

4,通过代码发现performLaunchActivity调用了当前正在打开的Activity的attach方法,而这个方法需要一个NonConfigurationInstances类型的参数,这个参数里面就有我们的ViewModelStore实例

Activity{
     final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
        attachBaseContext(context);
        mFragments.attachHost(null /*parent*/);
        //赋值给mLastNonConfigurationInstances
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        setAutofillOptions(application.getAutofillOptions());
        setContentCaptureOptions(application.getContentCaptureOptions());
    }
    public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }
}
ComponentActivity{
    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) {
            //先去lastNonConfigurationInstance中取,没有再创建
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }
}

5,在attach方法里就把这个参数赋值给mLastNonConfigurationInstances,当我们获取ViewModelStore实例的时候,就会先去mLastNonConfigurationInstances中取,如果没有再自己创建一个ViewModelStore实例,到这里整个调用链就搞明白了

生命周期绑定

ComponentActivity{
    public ComponentActivity(){
        //订阅生命周期,当生命周期==ON_DESTROY,清除ViewModel数据
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    if (!isChangingConfigurations()) {
                        getViewModelStore().clear();
                    }
                }
            }
        });
    }
}
ViewModelStore{
    public final void clear() {
        //遍历所有ViewModel并调用其clear()方法清空数据
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        //清空所有ViewModel
        mMap.clear();
    }
}

总结

1,ComponentActivity实现了ViewModelStoreOwner接口并实现了其抽象方法getViewModelStore()

2,我们通过ViewModelProvider使用默认工厂创建了ViewModel,通过唯一key值进行标识并保存到ViewModelStore中

3,当切换横竖屏的时候ActivityThread接收到RELAUNCH_ACTIVITY消息,就会调用Activity的retainNonConfigurationInstances()方法获取我们的ViewModelStore实例并保存起来

4,当Activity启动并调用attach()方法时就将切换横竖屏前的ViewModel恢复过来

5,当我们获取ViewModelStore实例的时候会调用先getLastNonConfigurationInstance()方法去取ViewModelStore,如果为null就会重新创建ViewModelStore并存储在全局中

6,当生命周期发生改变,并且状态为ON_DESTROY,清除ViewModel数据以及实例

到此这篇关于Android Jetpack库剖析之ViewModel组件篇的文章就介绍到这了,更多相关Android ViewModel内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android Jetpack架构组件 ViewModel详解

    前言 前面两篇文章我们已经学习了Lifecycle和DataBind,本篇文章我们来学习Jetpack系列中比较重要的ViewModel,Jetpack的很多很多组件都是搭配使用的,所以单独的知识点可能会有些"无意义"但却是我们项目实战的基础! ViewModel的使用 ViewModel类旨在以注重生命周期的方式存储和管理界面相关的数据.ViewModel类让数据可在发生屏幕旋转等配置更改后继续存在.这句话很好理解,还记得我们在讲解Lifecycle的时候 举的例子吗,我们还是使用那

  • 一文了解Android ViewModelScope 如何自动取消协程

    先看一下 ViewModel 中的 ViewModelScope 是何方神圣 val ViewModel.viewModelScope: CoroutineScope get() { val scope: CoroutineScope? = this.getTag(JOB_KEY) if (scope != null) { return scope } return setTagIfAbsent(JOB_KEY, CloseableCoroutineScope(SupervisorJob() +

  • Android ViewModel的使用总结

    目录 基本使用 MainRepository MainViewModel MainActivity ViewModel 相关问题是高频面试题.主要源于它是 MVVM 架构模式的重要组件,并且它可以在因配置更改导致页面销毁重建时依然保留 ViewModel 实例. 看看 ViewModel 的生命周期 ViewModel 只有在正常 Activity finish 时才会被清除. 问题来了: 为什么Activity旋转屏幕后ViewModel可以恢复数据 ViewModel 的实例缓存到哪儿了 什

  • 解决android viewmodel 数据刷新异常的问题

    3年的wpf开发经验,自认为对数据驱动UI开发模式的使用不是问题,但当开始研究android的mvvm模式开发时,发现两年多的android开发经验已经将之前的wpf开发忘得7788了.感慨一下:人老了,记忆力就这么脆弱. 谈正题:adroid mvvm开发模式 之 viewmodel使用小麻烦. viewmodel public class MyViewModel extends ViewModel { private MutableLiveData<List<User>> mU

  • Android-ViewModel和LiveData使用详解

    ViewModel类的设计目的是以一种关注生命周期的方式存储和管理与UI相关的数据. 例如:Activity在配置发生改变时(屏幕旋转),Activity就会重新创建,onCreate()方法也会重新调用.我们可以在onSaveInstanceState()方法中保存数据,并从onCreate()方法中通过Bundle恢复数据,但这种方法只适用于可以对其进行序列化的少量数据,而不适用于潜在的大量数据.使用ViewModel的话ViewModel会自动保留之前的数据并给新的Activity或Fra

  • Github简单易用的 Android ViewModel Retrofit框架

    目录 RequestViewModel Gradle 使用 1.retrofit接口的声明 2.retrofit配置 3.在Activity或Fragment中创建请求对象 4.继承RequestLiveData,处理返回数据 5.使用RequestViewModel和RequestLiveData请求数据 6.设置请求参数,主动请求数据 7.观察RequestLvieData数据变化 8.日志打印 RequestViewModel 优势: 快捷.方便地使用ViewModel .LiveData

  • Android通过ViewModel保存数据实现多页面的数据共享功能

    通过ViewModel实现的数据共享符合Android的MVC设计模式,将数据独立出来 实现的Demo 1.主页面通过SeekBar 来改变数字的值 2.点击进入就进入第二个界面,但是数据还是共享的 3.随便加两个数字上去,再次切换 4.发现数据还是共享的 下面是具体实现步骤: 1.建立两个Fragment(使用了Binding 和 Navigation) 一点要添加Binding 和 Navigation 不然做不了 2.建立一个继承于ViewModel的类 3.分别在两个Fragment的代

  • Android Jetpack库剖析之ViewModel组件篇

    前言 今天让我们一起去探究一下ViewModel的实现原理,描述的不对或不足还请海涵,仅作为参考 ViewModel简介 ViewModel是一个可感知Activity或Fragment生命周期的一个架构组件,当视图销毁,数据也会被清除,所以它的本质就是用来存储与视图相关的数据,让视图显示控制与数据分离,即使界面配置发生改变数据也不会被销毁,通常配合LiveData使用 ViewModel用法 class MainActivity : AppCompatActivity() { override

  • Android Jetpack库剖析之LiveData组件篇

    目录 LiveData简介 LiveData用法 数据订阅过程 PostValue过程 SetValue过程 生命周期变化 LiveData简介 在日常安卓开发中,一些耗时的操比如列网络请求,数据库读写都不能在主线程执行,必须开一条子线程去执行这些耗时操作,但我们往往需要在这些耗时操作执行完毕后更新UI,但安卓不能在子线程进行UI的更新,这时我们只能通过创建一个Handler来切回到主线程进行UI的更新,直到LiveData出现,LiveData是一个可被观察的数据容器,它将数据包装起来,使数据

  • Android Jetpack库剖析之Lifecycle组件篇

    目录 提纲 什么是Lifecycle 如何使用Lifecycle 关系梳理 Activity是如何实现Lifecycle的 CompatActivity AppCompatActivity Fragment是如何实现Lifecycle的 Lifecycle是如何下发宿主生命周期给观察者的 提纲 1,什么是Lifecycle? 2,如何使用Lifecycle? 3,LifecycleOwner,Lifecycle,LifecycleObserver之间是什么关系? 3,Activity是如何实现L

  • Android Jetpack库重要组件WorkManager的使用

    目录 前言 后台处理指南 后台处理面临的挑战 如何选择合适的后台解决方案 WorkManager概述 WorkManager使用 1 声明依赖项 2 自定义一个继承自Worker的类 3 选择worker执行的条件 4 下面贴出自定义worker类的全部源码 5 执行任务的方式 6 取消任务的执行 前言 WorkManager是Jetpack很重要的一个组件: 本篇我们就先来讲讲它是如何使用的,在讲解之前我们先了解关于后台处理的一些痛点 后台处理指南 我们知道每个 Android 应用都有一个主

  • Android Jetpack组件中LifeCycle作用详细介绍

    目录 Jetpack 1.那么Jetpack是什么呢 2.为何使用Jetpack 3.Jetpack与AndroidX LifeCycle 1.LifeCycle的作用 2.LifeCycle应用 1.设计组件 2.使用组件 3.总结LifeCycle的使用 Jetpack Jetpack,我觉得翻译为“飞行器”更好听,因为Google针对编程历史乱象,整理出一套组件库,帮助开发者创造更完美的应用作品.现在市面上,很多公司招聘面试要求渐渐把Jetpack看作必会技能,Google也在疯狂的安利J

  • Android Jetpack组件支持库DataBinding与ViewModel与LiveData及Room详解

    目录 一.官方推荐的Jetpack架构 二.添加依赖 三.创建Repository 四.创建ViewModel 五.activity中使用 Android Jetpack之ViewModel.LiveData Android Jetpack之LifeCycle 一.官方推荐的Jetpack架构 ViewModel是介于View(视图)和Model(数据模型)之间的中间层,能够使视图和数据分离,又能提供视图和数据之间的通信. LiveData是一个能够在ViewModel中数据发生变化时通知页面刷

  • Android Jetpack组件库LiveData源码深入探究

    目录 前言 一.LiveData 二.使用案例 三.LiveData 实现原理 四.LiveData 相关源码 五.LiveData分发问题 Android Jetpack之ViewModel.LiveData Android Jetpack之LifeCycle Android Jetpack之DataBinding+ViewModel+LiveData+Room 前言 Jetpack是一个由多个技术库组成的套件,可帮助开发者遵循最佳做法,减少样板代码并编写可在各种Android版本和设备中一致

  • Android JetPack组件的支持库Databinding详解

    目录 简介 启用databinding 布局xml variable (变量标签) data (数据标签) @{}表达式 绑定普通数据 绑定可观察数据 对单个变量的绑定-fields 对集合的绑定-collections 绑定对象-objects 绑定LiveData 双向绑定 简介 DataBinding 是 Google 在 Jetpack 中推出的一款数据绑定的支持库,利用该库可以实现在页面组件中直接绑定应用程序的数据源.使其维护起来更加方便,架构更明确简介. DataBinding 唯一

  • Android Jetpack组件ViewModel基本用法详解

    目录 引言 一.概述与作用 二.基本用法 小结 引言 天道好轮回,终于星期五,但是还是忙碌了一天.在项目中,我遇到了一个问题,起因则是无法实时去获取信息来更新UI界面,因为我需要知道我是否获取到了实时信息,我想到的办法有三,利用Handler收发消息在子线程与主线程切换从而更新信息,其二则是利用在页面重绘的时候(一般是页面变动如跳转下个页面和将应用切至后台),其三就是利用Jetpack中最重要的组件之一ViewModel,最后我还是选择了ViewModel,因为感觉更方便. 其实想到的前面两个方

随机推荐