Android
开发近年愈来愈趋于成熟稳定,不少第三方组件也愈来愈完善,上半年 Google
爸爸发布了名为 Jetpack
的工具包堪称应用开发者的福音,这里就讨论一下这个开发套件中 ViewModel
究竟是个什么样的存在。前端
先说一下 Jetpack,官方对此介绍是帮助开发者专一应用的业务代码、更轻松地开发出色的应用程序的一个 Android 软件开发组件集合java
Jetpack is a collection of Android software components to make it easier for you to develop great Android apps. These components help you follow best practices, free you from writing boilerplate code, and simplify complex tasks, so you can focus on the code you care about.android
里面包含了几乎全部开发 Android App 能用到的涉及程序架构、导航、UI、行为相关的组件库git
AppCompat
, Android KTX
, Multidex
, Test
Data Binding
, Lifecycles
, LiveData
, Navigation
, Paging
, Room
, ViewModel
, WorkManager
Download manager
, Media & playback
, Notifications
, Permissions
, Preferences
, Sharing
, Slices
Animation & transitions
, Auto
, Emoji
, Fragment
, Layout
, Palette
, TV
, Wear OS by Google
在这里就来分析一下 ViewModel
到底应该怎么使用github
讨论 ViewModel 以前须要先弄清楚一个问题,那就是一般在 Android App 开发中使用软件应用程序架构。
Google 爸爸一样发布了一系列软件架构的示例参考 👉 android-architecture缓存
在 8012 年的今天,MVC 这种上古时代的移动软件架构应该已经淘汰的差很少了,先说一下 MVP 这个如今几乎软件必备的软件架构。网络
所谓 MVP
就是把程序代码 按照 model-view-presenter 分层解耦
Presenter
一般是经过对 网络层调用、数据解析、业务处理以及其余逻辑代码的封装
而后经过对 View
层接口的调用来通知 UI
界面更新
经过将 数据、视图、逻辑 三层代码隔离来达到解耦的目的
可是这种架构有个问题,那就是 Presenter
很是依赖 View
层的接口架构
这种模式下,逻辑-视图之间的通信彻底依赖于 View 层的接口,一旦业务发生了变动,须要修改 View 层接口的声明、Presenter 对 View 通知调用的入口、以及 Activity
/ Fragment
等视图接口的实现app
那么在这种状况下 Android
开发的关注焦点转移到了 MVVM
架构模式
这种模式实际上是对 MVC
的另外一层变种,这里的 VM
指的就是 ViewModel
(跟下面讨论的 Architecture ViewModel 不同)
区别于 MVP
对业务逻辑的彻底抽离,MVVM
在视图之上抽象出了一个 ViewModel
的概念,这个层面关注了界面视图所关联的 数据 + 逻辑 的操做ide
有过网页前端或者 iOS 相关开发经验的同窗应该可以了解,其实就是 Data Binding 这种开发模式
经过将数据绑定到视图层,更新相应的数据 Model 来触发UI视图的状态更新
一个典型的场景就是基于 React
环境的开发,经过改变组件的 props
和 state
来通知 UI 视图刷新显示内容
关于 MVVM
的更多内容这里不展开细说,下面开始讨论一下 Android Jetpack
架构组件中 ViewModel
的使用
ViewModel 这个组件是什么?
官方介绍是这样的:
The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.
这里明确说明了 ViewModel 是出于管理和存储 UI 相关数据的目的,经过对 Android 组件生命周期感知的方式而设计出来的一个组件,而且容许在设备的配置发生变动(如屏幕旋转)时继续保持。
这样的话最直接的好处就是开发者再也不须要关心 Activity
/ Fragment
的相关状态数据持久化(saveInstanceStatus)的问题了,可以专一于产品业务的开发,避免缓存恢复数据这种重复模板化操做。
这是官方文档上对 ViewModel 如何工做的示意图 👇
能够看到 ViewModel
跟随 Activity
建立以后会一直在在内存中生存而且工做
直到与其关联的 Activity
销毁 destroy
时,会触发一个叫 onCleared
的生命周期函数,最后宣告死亡。
ViewModel 的生命周期感知依赖于 Jetpack
的另外一个组件 Lifecycles 关于 Lifecycles
这里不详细展开,只需明确 Lifecycles
经过对 Activity
生命周期的捕获来触发生命周期事件的上报来达到监控生命周期的目的
基于 Lifecycles
意味着开发者再也不须要手动关注组件的生命周期问题,那么 ViewModel
是如何感知而且同步应用组件的生命周期呢?
经过查看 ViewModel
源代码能够看出,它自己其实就是一个普通的 Java Bean,没有任何复杂的地方
public abstract class ViewModel {
/** * This method will be called when this ViewModel is no longer used and will be destroyed. * <p> * It is useful when ViewModel observes some data and you need to clear this subscription to * prevent a leak of this ViewModel. */
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
}
复制代码
那么问题来了,这样只怎么实现数据和生命周期的管理呢
翻看 viewmodel 的源代码会发现这个库实在是简单到了极点,只有几个类和接口,这就是为何要使用 Jetpack
组件是应该依赖 AppCompat
兼容套件了
由于 Jetpack
实现生命周期相关的组件实际上是基于 support-v4 来实现,经过 FragmentActivity
来实现组件相关的接口来完成对 ViewModel
的管理的
至于具体是怎么实现这个管理过程,这里暂不细讲,有兴趣能够自行阅读 FragmentActivity
的源码
要使用 ViewModel
来管理数据,首先须要声明一个 model 类来集成 android.arch.lifecycle.ViewModel
class SampleModel : ViewModel() {
var profile: UserInfo? = null
override fun onCleared() {
profile = null
}
}
复制代码
回到正题,实际开发中要怎么来使用 ViewModel 呢? 库中提供了一个 ViewModelProviders
的工具类来帮助建立 ViewModel
class SampleActivity : AppCompatActivity() {
val viewModel by lazy { ViewModelProviders.of(activity).get(SimpleModel::class.java) }
…
}
复制代码
这段代码在须要使用到 viewmodel 的时候经过 ViewModelProfiders#of(FragmentActivity) 来建立一个 ViewModelProvider
,而后调用 get
方法建立出所须要的 model
实例
很是简单地代码,到这里 viewmodel 看上去好像已经说完了
…
盐鹅怎么可能呢,讲到这里好像跟 MVVM
仍是没什么太大的关系
那么这里须要介绍 Jetpack Architecture 的另外一个重要的组件 LiveData 了
仍是先看官方说明
LiveData is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state.
这里已经清晰的介绍了 LiveData 是一个可观察的数据持有类,并且跟普通的观察者不同的地方在于
LiveData 能够感知 Android 系统组件生命周期,而且只在观察者组件处于活跃状态的时候才通知更新
看到这里是否是就想起来前面说的数据绑定操做? 没错,这样代码就变成了介个样子
class SampleModel : ViewModel() {
val profileData = MutableLiveData<UserInfo>()
var profile: UserInfo? = null
override fun onCleared() {
profile = null
}
fun fetchUserInfo() {
…
// 网络或本地持久化数据获取用户信息
…
// 获取成功后使用 LiveData 通知订阅端
profileData.postValue(profile)
}
}
class SampleActivity : AppCompatActivity() {
val viewModel by lazy { ViewModelProviders.of(activity).get(SampleModel::class.java) }
…
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
…
viewModel.profileData.observe(this, Observer {
it?.also {
// it -> 接收到的 UserInfo
…
…
/* 更新 UI */
}
})
}
}
复制代码
这样一来,ViewModel
只须要扮演数据持有者以及相关业务控制器,不用像 Presenter 那样须要关心 View 层的接口变化了,并且可以有效避免因 Presenter 持有 View 引用形成的内存泄漏问题
这里简单看一下 LiveData
的代码
/** * Posts a task to a main thread to set the given value. So if you have a following code * executed in the main thread: * <pre class="prettyprint"> * liveData.postValue("a"); * liveData.setValue("b"); * </pre> * The value "b" would be set at first and later the main thread would override it with * the value "a". * <p> * If you called this method multiple times before a main thread executed a posted task, only * the last value would be dispatched. * * @param value The new value */
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
/** * Sets the value. If there are active observers, the value will be dispatched to them. * <p> * This method must be called from the main thread. If you need set a value from a background * thread, you can use {@link #postValue(Object)} * * @param value The new value */
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
复制代码
能够看出 LiveData 经过设置 value 来驱动数据变化事件,内部经过分发新的 data 通知到订阅者(一般是UI界面的数据渲染代码)
这里有两个相关的方法:setValue
以及 postValue
查看 postValue
方法最后一行能够看出经过 post 来改变数据时,内部会经过一个任务调度器来事件分发到主线程,
而 setValue
方法经过注解声明以及首行的断言检查可知,此方法须要在主线程中调用,这意味着订阅的 Observer 始终会在主线程中响应
这两个方法的可见性修饰是 protected
,意味着开发者不能直接调用,因此 LiveData 提供了一个子类 MutableLiveData
/** * {@link LiveData} which publicly exposes {@link #setValue(T)} and {@link #postValue(T)} method. * * @param <T> The type of data hold by this instance */
public class MutableLiveData<T> extends LiveData<T> {
@Override
public void postValue(T value) {
super.postValue(value);
}
@Override
public void setValue(T value) {
super.setValue(value);
}
}
复制代码
代码很是简单,只是单纯的修改了这个两个更新数据的方法的修饰,暴露给了开发者调用
当咱们须要改变数据的时候只须要调用一下 postValue
方法就能够通知视图订阅来更新视图显示的内容和状态了,就像 React 中 对 props
和 stat
赋值同样简单
因此 … 赶忙开始拥抱、以及享受 ViewModel + LiveData 带来的便捷吧