iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >Android Jetpack库剖析之ViewModel组件篇
  • 478
分享到

Android Jetpack库剖析之ViewModel组件篇

2024-04-02 19:04:59 478人浏览 安东尼
摘要

前言 今天让我们一起去探究一下ViewModel的实现原理,描述的不对或不足还请海涵,仅作为参考 ViewModel简介 ViewModel是一个可感知Activity或Fragme

前言

今天让我们一起去探究一下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 );
        }
        if (!r.stopped) {
            callActivityOnStop(r, true , 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 );
        }
        if (!r.stopped) {
            callActivityOnStop(r, true , 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 );
        //赋值给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内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Android Jetpack库剖析之ViewModel组件篇

本文链接: https://www.lsjlt.com/news/164988.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android Jetpack库剖析之ViewModel组件篇
    前言 今天让我们一起去探究一下ViewModel的实现原理,描述的不对或不足还请海涵,仅作为参考 ViewModel简介 ViewModel是一个可感知Activity或Fragme...
    99+
    2022-11-13
  • Android Jetpack库剖析之LiveData组件篇
    目录LiveData简介LiveData用法数据订阅过程PostValue过程SetValue过程生命周期变化LiveData简介 在日常安卓开发中,一些耗时的操比如列网络请求,数据...
    99+
    2022-11-13
  • Android Jetpack组件之ViewModel如何使用
    本文小编为大家详细介绍“Android Jetpack组件之ViewModel如何使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“Android Jetpack组件之ViewModel如何使用”文章能帮助大家解决疑惑...
    99+
    2023-07-05
  • AndroidJetpack库剖析之Lifecycle组件篇
    目录提纲什么是Lifecycle如何使用Lifecycle关系梳理Activity是如何实现Lifecycle的CompatActivityAppCompatActivityFrag...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作