探索Android架构模式中ViewModel

前言

我们中的大多数人都遇到过旋转手机并且应用程序崩溃或UI失去状态的问题。

解决它最简单的办法就是配置Activitypotrait模式,但这是一个很糟糕的做法。

幸运的是,Android团队在2017年Google I / O期间发布了ViewModel。

该ViewModel类被设计用来保存与UI相关的数据并且它是可以感知生命周期变化的,这使得它能够自动应对配置变化(例如屏幕旋转)带来的影响。

它有以下职责:

  • 主要职责是管理UI的数据
  • 处理Activity/ Fragment和应用程序的其余部分之间的通信
  • 在配置更改期间保留数据

注意:它永远应该不能访问您的视图层次结构或保留Activity或Fragment的引用。

添加组件到项目中

添加goolge maven仓库

在project级别下的build.gradle中添加

allprojects {    repositories {        jcenter()        google()    }}

在app下的build.gradle添加

def lifecycle_version = "1.1.1"    // ViewModel and LiveData    implementation "android.arch.lifecycle:extensions:$lifecycle_version"    // alternatively - just ViewModel    implementation "android.arch.lifecycle:viewmodel:$lifecycle_version" // use -ktx for Kotlin    // alternatively - just LiveData    implementation "android.arch.lifecycle:livedata:$lifecycle_version"    // alternatively - Lifecycles only (no ViewModel or LiveData).    //     Support library depends on this lightweight import    implementation "android.arch.lifecycle:runtime:$lifecycle_version"    annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version"    // alternately - if using Java8, use the following instead of compiler    implementation "android.arch.lifecycle:common-java8:$lifecycle_version"    // optional - ReactiveStreams support for LiveData    implementation "android.arch.lifecycle:reactivestreams:$lifecycle_version"    // optional - Test helpers for LiveData    testImplementation "android.arch.core:core-testing:$lifecycle_version"

实现一个简单的ViewModel

public class MyViewModel extends ViewModel {    private MutableLiveData> users;    public LiveData> getUsers() {        if (users == null) {            users = new MutableLiveData>();            loadUsers();        }        return users;    }    private void loadUsers() {        // 执行一步操作获取users数据    }}

然后在Activity或者Fragment中获取数据

public class MyActivity extends AppCompatActivity {    public void onCreate(Bundle savedInstanceState) {        // 在系统第一次调用onCreate()方法时创建一个ViewModel        // 重新创建        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);        model.getUsers().observe(this, users -> {            // 更新UI        });    }}

如果ViewModel的宿主Activity被重新创建,那么该Activity可以收到自己第一次创建时一个同样的ViewModel。当宿主Activity被finished掉的时候,框架会自动帮我们调用ViewModel对象的onCleard 方法帮助我们清理资源。

注意:一个ViewMode类l一定不要持有view,生命周期的引用或任何可能持有对Activity上下文的引用的类

ViewModel对象可以包含LifecycleObservers,比如LiveData

如果 ViewModel需要 Application上下文,例如查找系统服务,则可以扩展AndroidViewModel 该类并在构造函数中接收Application 构造函数,因为Application类会扩展Context

ViewModel的生命周期

ViewModel对象被限定在通过ViewModelProvider获得ViewModel的生命周期里。ViewModel保留在内存中,直到其生命周期结束:在Activity下是在finishes的时候,在Fragment下是detached的时候。

ViewModelProvider

我们通常会在Activity的onCreate方法中请求创建ViewModel对象,但是系统可能在Activity的生命周期内多次调用onCreate方法,比如 屏幕旋转。但是ViewModel对象会一直存在于从第一次获取ViewMode对象直到Activity的finisheddestroyed

在Fragment之间共享数据

Activity中两个或多个Fragment之间需要通信是很常见的。这些Fragment可以使用同一个Activity作用范围下的ViewModel来处理通信。

public class SharedViewModel extends ViewModel {    private final MutableLiveData selected = new MutableLiveData();    public void select(Item item) {        selected.setValue(item);    }    public LiveData getSelected() {        return selected;    }}public class MasterFragment extends Fragment {    private SharedViewModel model;    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);        itemSelector.setOnClickListener(item -> {            model.select(item);        });    }}public class DetailFragment extends Fragment {    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);        model.getSelected().observe(this, { item ->           // Update the UI.        });    }}

注意这两个Fragment在使用ViewModelProvider获取ViewModel时是用的getActivity()。因此,这两个Fragment都会收到相同的SharedViewModel实例,这个实例作用域是Activity。

这种方法提供了以下好处:

  • 这个Activity不需要做任何事情,也不需要了解有关此通信的任何信息
  • Fragment之间不需要了解彼此,除了SharedViewModel的联系。如果一个fragment消失了,其他fragment还可以继续正常工作
  • 每个Fragment都有其自己的生命周期,并且不受其他生命周期的影响。如果一个Fragment替换另一个Fragment,UI将继续工作而不会出现任何问题

初探ViewModel原理

MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);

ViewModelProviders 其实是ViewModelStore的一个实用类,它在内部引用ViewModelStore返回ViewModel实例(如果存在,否则就创建一个并保存下来)。

ViewModelStore内部使用HashMap跟踪ViewModel。

public class ViewModelStore {    private final HashMap mMap = new HashMap();    public ViewModelStore() {}    final void put(String key, ViewModel viewModel) {        ViewModel oldViewModel = (ViewModel)this.mMap.get(key);        if(oldViewModel != null) {            oldViewModel.onCleared();        }        this.mMap.put(key, viewModel);    }    final ViewModel get(String key) {        return (ViewModel)this.mMap.get(key);    }    public final void clear() {        Iterator var1 = this.mMap.values().iterator();        while(var1.hasNext()) {            ViewModel vm = (ViewModel)var1.next();            vm.onCleared();        }        this.mMap.clear();    }}

结论

ViewModels对于从用户界面分离数据非常有用,这些数据在测试过程中受益,并且配置更改仍然存在,这是在您的应用中使用ViewModel组件的一个重要理由。它甚至可以自己清除资源是另一个重要因素。

最后,它使得在Fragment之间通信更容易。

利用ViewModels,您无需担心在屏幕旋转或其他配置更改期间应用程序崩溃或丢失UI状态

参考

https://developer.android.com/topic/libraries/architecture/viewmodel

其他

Android架构组件(Architecture Components)介绍

Android架构组件—LiveData

Android架构组件—ViewModel

Android架构组件Room的使用

更多相关文章

  1. Android之数据存储详解(二)之SQLite数据库存储数据
  2. Android数据存储方式(一)文件
  3. Android Service 浅析(生命周期,启动方式,前台Service)
  4. Android客户端采用Http 协议Post方式请求与服务端进行数据交互
  5. Android app widget 支持的Layout和widget组件
  6. Android Jetpack组件学习 Room
  7. android Json数据构建于解析
  8. Android SQLite数据库解析并使用两种方法实现增删改查

随机推荐

  1. SpringBoot 与 Kotlin 完美交融
  2. 值得关注的 Vue.js开源项目[每日前端夜话
  3. 「面试题」介绍你做过最复杂的系统
  4. 如何健壮你的后端服务
  5. 技术探讨的正确姿势
  6. 谷歌助力,快速实现 Java 应用容器化
  7. Java 12 正式发布
  8. 探寻 Redis 内存诡异增长的元凶
  9. npm 和 yarn 你选哪个?[每日前端夜话0x100
  10. 人人都是 API 设计者:我对 RESTful API、G