Android架构组件JetPack之ViewModel(二)

Android架构组件JetPack之ViewModel(二),第1张

概述阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680概述ViewModel,从字面上理解的话,它肯定是跟视图(View)以及数据(Model)相关的。正像它字面意思一样,它是负责准备和管理和UI组件(Fragment/Activity)相关的数据类,也就是说ViewModel是

阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680

概述

viewmodel,从字面上理解的话,它肯定是跟视图(VIEw)以及数据(Model)相关的。正像它字面意思一样,它是负责准备和管理和UI组件(Fragment/Activity)相关的数据类,也就是说viewmodel是用来管理UI相关的数据的,同时viewmodel还可以用来负责UI组件间的通信。

之前存在的问题

viewmodel用来存储和管理UI相关的数据,可于将一个Activity或Fragment组件相关的数据逻辑抽象出来,并能适配组件的生命周期,如当屏幕旋转Activity重建后,viewmodel中的数据依然有效。

引入viewmodel之前,存在如下几个问题:

通常AndroID系统来管理UI controllers(如Activity、Fragment)的生命周期,由系统响应用户交互或者重建组件,用户无法 *** 控。当组件被销毁并重建后,原来组件相关的数据也会丢失,如果数据类型比较简单,同时数据量也不大,可以通过onSaveInstanceState()存储数据,组件重建之后通过onCreate(),从中读取Bundle恢复数据。但如果是大量数据,不方便序列化及反序列化,则上述方法将不适用。
UI controllers经常会发送很多异步请求,有可能会出现UI组件已销毁,而请求还未返回的情况,因此UI controllers需要做额外的工作以防止内存泄露。
当Activity因为配置变化而销毁重建时,一般数据会重新请求,其实这是一种浪费,最好就是能够保留上次的数据。
UI controllers其实只需要负责展示UI数据、响应用户交互和系统交互即可。但往往开发者会在Activity或Fragment中写许多数据请求和处理的工作,造成UI controllers类代码膨胀,也会导致单元测试难以进行。我们应该遵循职责分离原则,将数据相关的事情从UI controllers中分离出来。

viewmodel基本使用

public class Myviewmodel extends viewmodel {    private mutablelivedata<List<User>> users;    public liveData<List<User>> getUsers() {        if (users == null) {            users = new mutablelivedata<List<Users>>();            loadUsers();        }        return users;    }    private voID loadUsers() {        // 异步调用获取用户列表    }}

新的Activity如下:

public class MyActivity extends AppCompatActivity {    public voID onCreate(Bundle savedInstanceState) {        Myviewmodel model = viewmodelProvIDers.of(this).get(Myviewmodel.class);        model.getUsers().observe(this, users -> {            // 更新 UI        });    }}

如果Activity被重新创建了,它会收到被之前Activity创建的相同Myviewmodel实例。当所属Activity终止后,框架调用viewmodel的onCleared()方法清除资源。

因为viewmodel在指定的Activity或Fragment实例外存活,它应该永远不能引用一个VIEw,或持有任何包含Activity context引用的类。如果viewmodel需要Application的context(如获取系统服务),可以扩展AndroIDviewmodel,并拥有一个构造器接收Application。

在Fragment间共享数据

一个Activity中的多个Fragment相互通讯是很常见的。之前每个Fragment需要定义接口描述,所属Activity将二者捆绑在一起。此外,每个Fragment必须处理其他Fragment未创建或不可见的情况。通过使用viewmodel可以解决这个痛点,这些Fragment可以使用它们的Activity共享viewmodel来处理通讯:

public class Sharedviewmodel extends viewmodel {    private final mutablelivedata<Item> selected = new mutablelivedata<Item>();    public voID select(Item item) {        selected.setValue(item);    }    public liveData<Item> getSelected() {        return selected;    }}public class MasterFragment extends Fragment {    private Sharedviewmodel model;    public voID onActivityCreated() {        model = viewmodelProvIDers.of(getActivity()).get(Sharedviewmodel.class);        itemSelector.setonClickListener(item -> {            model.select(item);        });    }}public class DetailFragment extends lifecycleFragment {    public voID onActivityCreated() {        Sharedviewmodel model = viewmodelProvIDers.of(getActivity()).get(Sharedviewmodel.class);        model.getSelected().observe(this, { item ->           // update UI        });    }}

注意:上面两个Fragment都用到了如下代码来获取viewmodel,getActivity()返回的是同一个宿主Activity,因此两个Fragment之间返回的是同一个Sharedviewmodel对象。

Sharedviewmodel model = viewmodelProvIDers.of(getActivity()).get(Sharedviewmodel.class);

这种方式的好处包括:

1.Activity不需要做任何事情,也不需要知道通讯的事情
2.Fragment不需要知道彼此,除了Sharedviewmodel进行联系。如果它们(Fragment)其中一个消失了,其余的仍然能够像往常一样工作。
3.每个Fragment有自己的生命周期,而且不会受其它Fragment生命周期的影响。事实上,一个Fragment替换另一个Fragment,UI的工作也不会受到任何影响。

viewmodel的生命周期

viewmodel对象的范围由获取viewmodel时传递至viewmodelProvIDer的lifecycle所决定。viewmodel始终处在内存中,直到lifecycle永久地离开—对于Activity来说,是当它终止(finish)的时候,对于Fragment来说,是当它分离(detached)的时候。

 

 ​ 

 

上图左侧为Activity的生命周期过程,期间有一个旋转屏幕的 *** 作;右侧则为viewmodel的生命周期过程。

一般通过如下代码初始化viewmodel:

viewmodel = viewmodelProvIDers.of(this).get(UserProfileviewmodel.class);

this参数一般为Activity或Fragment,因此viewmodelProvIDer可以获取组件的生命周期。

Activity在生命周期中可能会触发多次onCreate(),而viewmodel则只会在第一次onCreate()时创建,然后直到最后Activity销毁。

viewmodel相关类图

 ​ 

viewmodelProvIDers是viewmodel工具类,该类提供了通过Fragment和Activity得到viewmodel的方法,而具体实现又是由viewmodelProvIDer实现的。

viewmodelProvIDer是实现viewmodel创建、获取的工具类。在viewmodelProvIDer中定义了一个创建viewmodel的接口类——Factory。viewmodelProvIDer中有个viewmodelStore对象,用于存储viewmodel对象。

viewmodelStore是存储viewmodel的类,具体实现是通过HashMap来保存VIEwModle对象。

viewmodel是个抽象类,里面只定义了一个onCleared()方法,该方法在viewmodel不在被使用时调用。viewmodel有一个子类AndroIDviewmodel,这个类是便于要在viewmodel中使用Context对象,因为我们前面提到不能在viewmodel中持有Activity的引用。

viewmodelStores是viewmodelStore的工厂方法类,它会关联HolderFragment,HolderFragment有个嵌套类——HolderFragmentManager。

viewmodel相关时序图

追溯创建一个viewmodel的源码,会察觉需要的步骤有点多。下面以在Fragment中得到viewmodel对象为例看下整个过程的时序图。

 

 ​ 

 

时序图看起来比较复杂,但是它只描述了两个过程:

得到viewmodel对象。
HolderFragment被销毁时,viewmodel收到onCleared()通知。

viewmodel相关源码分析

viewmodelProvIDers类的具体实现:

public class viewmodelProvIDers {    private static Application checkApplication(Activity activity) {        Application application = activity.getApplication();        if (application == null) {            throw new IllegalStateException("Your activity/fragment is not yet attached to "                    + "Application. You can't request viewmodel before onCreate call.");        }        return application;    }    private static Activity checkActivity(Fragment fragment) {        Activity activity = fragment.getActivity();        if (activity == null) {            throw new IllegalStateException("Can't create viewmodelProvIDer for detached fragment");        }        return activity;    }    @NonNull    @MainThread    public static viewmodelProvIDer of(@NonNull Fragment fragment) {        viewmodelProvIDer.AndroIDviewmodelFactory factory =                viewmodelProvIDer.AndroIDviewmodelFactory.getInstance(                        checkApplication(checkActivity(fragment)));        return new viewmodelProvIDer(viewmodelStores.of(fragment), factory);    }    @NonNull    @MainThread    public static viewmodelProvIDer of(@NonNull FragmentActivity activity) {        viewmodelProvIDer.AndroIDviewmodelFactory factory =                viewmodelProvIDer.AndroIDviewmodelFactory.getInstance(                        checkApplication(activity));        return new viewmodelProvIDer(viewmodelStores.of(activity), factory);    }    @NonNull    @MainThread    public static viewmodelProvIDer of(@NonNull Fragment fragment, @NonNull Factory factory) {        checkApplication(checkActivity(fragment));        return new viewmodelProvIDer(viewmodelStores.of(fragment), factory);    }    @NonNull    @MainThread    public static viewmodelProvIDer of(@NonNull FragmentActivity activity,            @NonNull Factory factory) {        checkApplication(activity);        return new viewmodelProvIDer(viewmodelStores.of(activity), factory);    }

viewmodelProvIDers提供了四个of()方法,四个方法功能类似,其中of(FragmentActivity activity, Factory factory)和of(Fragment fragment, Factory factory)提供了自定义创建viewmodel的方法。

判断Fragment的是否Attached to Activity,Activity的Application对象是否为空。创建viewmodel对象看似很简单,一行代码搞定。
new viewmodelProvIDer(viewmodelStores.of(fragment), factory)

先看看viewmodelStores.of()方法:

    @NonNull    @MainThread    public static viewmodelStore of(@NonNull Fragment fragment) {        if (fragment instanceof viewmodelStoreOwner) {            return ((viewmodelStoreOwner) fragment).getviewmodelStore();        }        return holderFragmentFor(fragment).getviewmodelStore();    }

继续深入发现其实是实现了一个接口:

public interface viewmodelStoreOwner {    @NonNull    viewmodelStore getviewmodelStore();}

holderFragmentFor()是HolderFragment的静态方法,HolderFragment继承自Fragment。我们先看holderFragment()方法的具体实现

    @RestrictTo(RestrictTo.Scope.liBRARY_GROUP)    public static HolderFragment holderFragmentFor(Fragment fragment) {        return sHolderFragmentManager.holderFragmentFor(fragment);    }    @NonNull    @OverrIDe    public viewmodelStore getviewmodelStore() {        return mviewmodelStore;    }

继续看HolderFragmentManager.holderFragmentFor()方法的具体实现

HolderFragment holderFragmentFor(Fragment parentFragment) {            FragmentManager fm = parentFragment.getChildFragmentManager();            HolderFragment holder = findHolderFragment(fm);            if (holder != null) {                return holder;            }            holder = mNotCommittedFragmentHolders.get(parentFragment);            if (holder != null) {                return holder;            }            parentFragment.getFragmentManager()                    .registerFragmentlifecycleCallbacks(mParentDestroyedCallback, false);            holder = createHolderFragment(fm);            mNotCommittedFragmentHolders.put(parentFragment, holder);            return holder;        }private FragmentlifecycleCallbacks mParentDestroyedCallback =    new FragmentlifecycleCallbacks() {        @OverrIDe        public voID onFragmentDestroyed(FragmentManager fm, Fragment parentFragment) {            super.onFragmentDestroyed(fm, parentFragment);            HolderFragment fragment = mNotCommittedFragmentHolders.remove(                    parentFragment);            if (fragment != null) {                Log.e(LOG_TAG, "Failed to save a viewmodel for " + parentFragment);            }        }    };private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {    HolderFragment holder = new HolderFragment(); // 创建HolderFragment对象    fragmentManager.beginTransaction().add(holder, HolDER_TAG).commitAllowingStateLoss();    return holder;}public HolderFragment() {    //这个是关键,这就使得Activity被recreate时,Fragment的onDestroy()和onCreate()不会被调用    setRetainInstance(true); }

setRetainInstance(boolean) 是Fragment中的一个方法。将这个方法设置为true就可以使当前Fragment在Activity重建时存活下来, 如果不设置或者设置为 false, 当前 Fragment 会在 Activity 重建时同样发生重建, 以至于被新建的对象所替代。

在setRetainInstance(boolean)为true的 Fragment 中放一个专门用于存储viewmodel的Map, 自然Map中所有的viewmodel都会幸免于Activity重建,让Activity, Fragment都绑定一个这样的Fragment, 将viewmodel存放到这个 Fragment 的 Map 中, viewmodel 组件就这样实现了。

到此为止,我们已经得到了VIEwStore对象,前面我们在创建viewmodelProvIDer对象是通过这行代码实现的new viewmodelProvIDer(viewmodelStores.of(fragment), sDefaultFactory)现在再看下viewmodelProvIDer的构造方法

public viewmodelProvIDer(@NonNull viewmodelStore store, @NonNull Factory factory) {        mFactory = factory;        this.mviewmodelStore = store;    }

现在就可以通过viewmodelProvIDer.get()方法得到viewmodel对象,继续看下该方法的具体实现

    @NonNull    @MainThread    public <T extends viewmodel> T get(@NonNull String key, @NonNull Class<T> modelClass) {        viewmodel viewmodel = mviewmodelStore.get(key); //从缓存中查找是否有已有viewmodel对象。        if (modelClass.isinstance(viewmodel)) {            //noinspection unchecked            return (T) viewmodel;        } else {            //noinspection StatementWithEmptyBody            if (viewmodel != null) {                // Todo: log a warning.            }        }        viewmodel = mFactory.create(modelClass); //创建viewmodel对象,然后缓存起来。        mviewmodelStore.put(key, viewmodel);        //noinspection unchecked        return (T) viewmodel;    }

viewmodelProvIDer.get()方法比较简单,注释中都写明了。最后我们看下viewmodelStore类的具体实现

public class viewmodelStore {    private final HashMap<String, viewmodel> mMap = new HashMap<>();    final voID put(String key, viewmodel viewmodel) {        viewmodel oldviewmodel = mMap.get(key);        if (oldviewmodel != null) {            oldviewmodel.onCleared();        }        mMap.put(key, viewmodel);    }    final viewmodel get(String key) {        return mMap.get(key);    }    public final voID clear() {        for (viewmodel vm : mMap.values()) {            vm.onCleared();        }        mMap.clear();    }}

viewmodelStore是缓存viewmodel的类,put()、get()方法用于存取viewmodel对象,另外提供了clear()方法用于清空缓存的viewmodel对象,在该方法中会调用viewmodel.onCleared()方法通知viewmodel对象不再被使用。

viewmodel收到onCleared()通知

HolderFragment的onDestroy()方法

    @OverrIDe    public voID onDestroy() {        super.onDestroy();        mviewmodelStore.clear();    }

在onDestroy()方法中调用了viewmodelStore.clear()方法,我们知道在该方法中会调用viewmodel的onCleared()方法。在你看了HolderFragment源码后,或许你会有个疑问,mviewmodelStore保存的viewmodel对象是在哪里添加的呢? 细心的话,你会发现在viewmodelProvIDer的构造方法中,已经将HolderFragment中的ViwModelStore对象mviewmodelStore的引用传递给了viewmodelProvIDer中的mviewmodelStore,而在viewmodelProvIDer.get()方法中会向mviewmodelStore添加viewmodel对象。

总结

viewmodel职责是为Activity或Fragment管理、请求数据,具体数据请求逻辑不应该写在viewmodel中,否则viewmodel的职责会变得太重,此处需要一个引入一个Repository,负责数据请求相关工作。具体请参考 AndroID架构组件。
viewmodel可以用于Activity内不同Fragment的交互,也可以用作Fragment之间一种解耦方式。
viewmodel也可以负责处理部分Activity/Fragment与应用其他模块的交互。
viewmodel生命周期(以Activity为例)起始于Activity第一次onCreate(),结束于Activity最终finish时。

原文链接:https://blog.csdn.net/qq_24442769/article/details/79426609
阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680

总结

以上是内存溢出为你收集整理的Android架构组件JetPack之ViewModel(二)全部内容,希望文章能够帮你解决Android架构组件JetPack之ViewModel(二)所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1067161.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-26
下一篇 2022-05-26

发表评论

登录后才能评论

评论列表(0条)

保存