浅谈ViewModel

1 主要功能

  • Activity、Fragment存活期间的数据存储;
  • bind同一Activity的多个Fragment间数据共享;
  • 独立或与LiveData配合实现代码解耦;

2 使用方法

1) 引入ViewModel

  1. 在build.gradle中添加编译lifecycle.extensions module,该module同时包含ViewModel和LiveData:
compile('android.arch.lifecycle:extensions:1.0.0')

由于lifecycle.extensions内部依赖26.1.0版本的support_v7包,建议将工程中已引用的support_v7组件版本也升级到26.1.0或以上。

2) 构造数据对象

  1. 自定义ViewModel类,继承ViewModel;
  2. 在自定义的ViewModel类中添加需要的数据对象;
public class DemoViewModel extends ViewModel { 
    final public DemoData mData = new DemoData(); 
    public DemoViewModel() {} 
}

3) 获取数据

  1. 通过ViewModelProviders和Activity / Fragment获取ViewModelProvider;
  2. 通过ViewModelProvider和自定义ViewModel类获取自定义ViewModel对象;
  3. 从自定义ViewModel对象获取数据对象,进行需要的读写操作。
DemoData data = ViewModelProviders.of(getActivity()) 
        .get(DemoViewModel.class)
        .mData; 
data.doSth();

3 使用场景示范

1) 单Activity多Fragment间数据维护

  • 需求点:
    • bind同一个Activity的Fragment A、B;
    • Fragment A、B间存在跳转关系;
    • Fragment A、B共同维护一些数据,且A、B均有读取、修改的业务需求;
  • 传统实现方法1:Intent +onFragmentResult() 大致流程:
    • 跳转时需要将数据按k-v封装入Bundle,或者将数据Parcelable传递给下个页面;
    • 下个页面修改数据并返回后,需要从onFragmentResult()的Intent解析并同步数据;
    • Intent传递的数据大小总量不能超过1M;

麻烦且数据大小有限制。

  • 传统实现方法2:数据对象SingleInstance、static 缺点:
    • 数据的生命周期要自行控制;
    • 容易内存泄漏;

同样麻烦,且有一定风险。

ViewModel同时规避了传统方法的缺点:

  • bind同一个Activity的Fragments均可以通过ViewModelProvider获取共同的数据对象,无需主动进行数据传递;
  • 脱离Intent、Bundle、Parcelable这些用起来很麻烦的控件;
  • 数据生命周期由ViewModel内部掌控,无需手动管理销毁;

2) 与LiveData配合实现UI、业务逻辑分层

同为官方架构组件的LiveData,功能是监听数据变化,并回调给注册的observer。ViewModel+LiveData可以很方便的抽象出数据层和业务层,快速解耦。 下面的Demo来自官方案例。通过Demo,以及LiveData、ViewModel同处一个module,可以看出官方非常建议两者搭配使用。再配合以往的Data-Binding,可以快速搭建起一套简易的MVVM业务体系。

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() { 
            //Do an asyncronous operation to fetch users. 
    } 
} 

public class MyActivity extends AppCompatActivity { 
    public void onCreate(Bundle savedInstanceState) { 
            //Create a ViewModel the first time the system calls an activity's onCreate()method. 
            //Re-created activities receive the same MyViewModel instance created by thefirst activity. 
          MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class); 
          model.getUsers().observe(this,users -> { 
                //update UI 
          });
     } 
}

4 生命周期特性

ViewModel的生命周期与Lifecycle同步,当Activity /Fragment超出Lifecycle范围(并不是onDestroy()回调),ViewModel连同其包含的数据一起被销毁了。具体有多大呢,按照官方说法:

in the case of an activity, when it finishes, whilein the case of a fragment, when it’s detached.

Activity退栈后(Fragment则是与Activity解除关系后),ViewModel就解除引用关系,准备被系统回收了。下面这张图片更直观的展示了ViewModel的生命周期:

5 源码分析

带着两个小问题简单的进行下源码分析:

1) ViewModel与Activity / Fragment的映射关系是如何建立起来的?

(以Activity为例)

第一部分:获取ViewModelProvider

  • 调用ViewModelProviders.of(Activity);
  • 一路调用,最终走到HolderFragmentManager.holderFragmentFor(Activity);
  • HolderFragmentManager依次从FragmentManager和HolderFragmentManager持有的HashMap查找:该Activity是否有HolderFragment与之映射(HolderFragment的作用下面一个问题将谈到)
HolderFragment.class 
HolderFragment holderFragmentFor(FragmentActivity activity) { 
     FragmentManagerfm = activity.getSupportFragmentManager(); 
     HolderFragment holder = findHolderFragment(fm); 
     if(holder != null) { 
            return holder; 
      }else { 
            holder = (HolderFragment)this.mNotCommittedActivityHolders.get(activity); 
            if(holder != null) { 
                  return holder; 
            }else { 
                 //创建HolderFragment
  • 有则返回该HolderFragment;没有则创建一个HolderFragment:
    1. 新建一个HolderFragment,commit给FragmentManager,并存入HashMap以标记为not committed状态,防止重复commit;
    2. 向Application注册对该Activity的生命周期监听。如果HolderFragment尚未create,Activity就已经销毁,则从HashMap中移除该Activity,防止泄露;
    3. HolderFragment成功创建后,从HashMap中移除该Activity;
  • 使用HolderFragment持有的ViewModelStore对象完成ViewModelProvider的创建;

第一部分的职责是构建 / 查找HolderFragment,对构建过程中的异常做保护,最后返回ViewModelProvider。HolderFragment和ViewModelProvider共同持有的ViewModelStore将成为第二部分的核心;

(第二部分:获取ViewModel)

  • 调用ViewModelProvider.get(ViewModel.class);
  • 通过ViewModel的规范名(canonical name),从HashMap中查找是否已经存在该ViewModel的实例。有则返回;没有则通过反射构建一个,并存入HashMap;
ViewModelStore.class 
public class ViewModelStore { 
  private final HashMap<String, ViewModel> mMap = new HashMap();

第二部分职责是映射,通过类名从HashMap查找ViewModel实例。整个映射逻辑也可以简化为:通过Activity类名找ViewModel实例;

2) ViewModel的生命周期如何管理?

ViewModel objects are scoped to the Lifecycle passed to the ViewModelProvider when getting the ViewModel.

看到官方文档中的这句话,心想ViewModel莫非是通过同为官方架构组件的Lifecycle来管理的生命周期的? 其实并没有那么复杂,ViewModel在第一次调用ViewModelProvider.get(ViewModel.class)创建;而销毁就需要靠HolderFragment自己的onDestroy()回调:

HolderFragment.class 
public void onDestroy() { 
        super.onDestroy();                this.mViewModelStore.clear();
}
ViewModelStore.class 
public final void clear() { 
        Iteratorvar1 = this.mMap.values().iterator(); 

        while(var1.hasNext()){ 
            ViewModel vm = (ViewModel)var1.next(); 
            vm.onCleared();
        } 

        this.mMap.clear();
    }

HolderFragment销毁后,调用ViewModelStore.clear(),清理HashMap对ViewModel对象的引用,待系统GC回收ViewModel。这也解释了创建ViewModelProvider时为什么需要HolderFragment配合,HolderFragment掌控了ViewModel的生命周期。

6 Last but not least

简述下官方架构中各组件的主要职责:

  • Lifecycle:将Activity /Fragment生命周期回调事件向外抛出;
  • LiveData:在Lifecycle范围内监听数据的变化;
  • ViewModel:在Lifecycle范围内存储和共享数据;
  • Room:简化Db操作;

除了Room,可以感受到官方在尽力把大家从最初的MVC往MVVM引导,更加强大的官方组件将使UI-业务-数据的抽象过程变得更加简单顺滑。

本文分享自微信公众号 - QQ音乐技术团队(gh_287053a877e6),作者:gerardzhang

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-01-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android Native 开发之 NewString 与 NewStringUtf 解析

    本文将从一个 Native Crash 分析入手,带大家了解我们平时开发中,那些容易忽略但又很值得学习的底层源码知识。

    QQ音乐技术团队
  • web前端好帮手 - Jest单元测试工具

    本文介绍如何使用Jest覆盖Web前端单元测试、如何统计测试覆盖率,Jest对比Mocha等内容。 Jest是什么? ? Jest是一个令人愉快的 JavaS...

    QQ音乐技术团队
  • Android O 新特性和行为变更总结

    多窗口是 Android 7.1 之后引入的(关于多窗口适配需要注意的地方,但是趁此机会了解一下 Android O 版本的新特性也不错,而且 Google I...

    QQ音乐技术团队
  • ViewModel 和 LiveData:为设计模式打 Call 还是唱反调?

    Android 开发者
  • Android Architecture Components(3)

    上一篇文章中我们介绍了Architecture Components中的LifeCycle,LifeCycleOwner及LifeCycleObserver,不...

    小海编码日记
  • 7、使用Java Low Level REST Client操作elasticsearch

    使用doc_as_upsert可以在文档不存在的时候,把doc中的内容插入到文档中。

    java乐园
  • 日计不足涓滴成河-自定义响应结果格式化器

    响应结果就是,在客户端向服务器发出请求后,服务器根据客户端的请求参数,给出的结果,这就是一个完整的响应结果过程。

    梁规晓
  • 使用VM Tools让VMware虚拟机里的ubuntu能够共享Windows系统的文件夹

    版权声明:本文为博主汪子熙原创文章,未经博主允许不得转载。 https://jerry.bl...

    Jerry Wang
  • 建筑师跨界无人驾驶技术,探索空间设计与智能技术的融合

    PIX是喻川的无人驾驶项目,他希望无人驾驶的移动方式会降低出行成本,消除出勤的焦虑与时间。只需手机上的几个按键就实现:

    mixlab
  • 建筑师跨界无人驾驶技术,探索空间设计与智能技术的融合

    PIX是喻川的无人驾驶项目,他希望无人驾驶的移动方式会降低出行成本,消除出勤的焦虑与时间。只需手机上的几个按键就实现:

    mixlab

扫码关注云+社区

领取腾讯云代金券