关于应用架构,Google官方现在主推MVVM架构,官方推出的JetPack库提供了一系类支持MVVM架构,其中最核心的两个类是ViewModel和LiveData。
在MVVM架构中,View通常指Activity和Fragment,主要用来根据数据渲染UI,而Model主要负责数据的获取,这里通常包含获取网络数据和本地缓存数据,而ViewModel作为View和Model的桥梁,主要负责UI数据的处理,官方提供了ViewModel类作为一种实现。LiveData作为一种可观察的数据存储类,可以很好在支持ViewModel处理好的数据通知UI的更新。
主要负责存储和管理和界面相关的数据。很多地方都提到ViewModel感知生命周期,查看源码可以得知并不是这样,它只不过在config变化导致Activity销毁重建期间不会销毁。
ViewModel对于开发者来说,就是Activity或Fragment的一个成员变量,它的初始化代码通常是这样的:
viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
而它的底层实现过程是这样的,FragmentActivity或Fragment有一个成员变量mViewModelStore用来存储ViewModel,ViewModelProviders只是个包装器,它会从当前activity或fragment的ViewModelStore中取ViewModel,如果没有的话就创建一个并存储到ViewModelStore中。至于文档中提到的config变化不会销毁,是因为config变化引起的销毁重建,FragmentActivity会缓存ViewModelStore,如下代码所示,在onRetainNonConfigurationInstance函数中保存了ViewModelStore。但是正常的内存不足页面销毁重建时,ViewModel是会重新创建的,我们可以看到onSaveInstanceState中没有对ViewModelStore做缓存的,并且在onDestroy中进行了销毁。
protected void onCreate(@Nullable Bundle savedInstanceState) {
this.mFragments.attachHost((Fragment)null);
super.onCreate(savedInstanceState);
FragmentActivity.NonConfigurationInstances nc = (FragmentActivity.NonConfigurationInstances)this.getLastNonConfigurationInstance();
if (nc != null && nc.viewModelStore != null && this.mViewModelStore == null) {
this.mViewModelStore = nc.viewModelStore;
}
......
}
@NonNull
public ViewModelStore getViewModelStore() {
if (this.getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the Application instance. You can't request ViewModel before onCreate call.");
} else {
if (this.mViewModelStore == null) {
FragmentActivity.NonConfigurationInstances nc = (FragmentActivity.NonConfigurationInstances)this.getLastNonConfigurationInstance();
if (nc != null) {
this.mViewModelStore = nc.viewModelStore;
}
if (this.mViewModelStore == null) {
this.mViewModelStore = new ViewModelStore();
}
}
return this.mViewModelStore;
}
}
//config变化时保存数据
public final Object onRetainNonConfigurationInstance() {
Object custom = this.onRetainCustomNonConfigurationInstance();
FragmentManagerNonConfig fragments = this.mFragments.retainNestedNonConfig();
if (fragments == null && this.mViewModelStore == null && custom == null) {
return null;
} else {
FragmentActivity.NonConfigurationInstances nci = new FragmentActivity.NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = this.mViewModelStore;
nci.fragments = fragments;
return nci;
}
}
1、简介
看官方文档我们知道LiveData是一种可观察的数据存储类,它具有感知生命周期的能力。通常ViewModel是靠LiveData通知View层数据变化的,Activity、Fragment和Service监听LiveData,当数据变化时会收到通知做相应的UI更新工作。上面说了LiveData有生命周期感知能力,所以当Activity处于非活跃状态时,是不会收到通知的,当Activity再次处于活跃状态时,会重新收到数据变化的通知。我们可以看下典型的使用的方式:
// void observe(LifecycleOwner owner, Observer super T> observer)
mViewModel?.phone?.observe(this, Observer {
user_phone.setText(it ?: "")
})
上面的代码是写在Activity或Fragment中的,phone就是ViewModel中的一个LiveData,我们可以看到在监听LiveData时同时传了参数LifecycleOwner,LiveData就是通过这个LifecycleOwner感知生命周期的,当phone的值发生变化并且当前页面(LifecycleOwner)处于活跃状态时,会收到通知更新UI,如果页面不处于活跃状态不会收到通知的。当页面从不活跃状态变为活跃状态时,中间数据发生过变化的话会收到通知。
2、使用
在UI层(Activity、Fragment)向LiveData注册监听者,接收数据变化的通知,代码如上面的图所示;LiveData没有公开的方法更新数据,子类MutableLiveData公开了两个方法用来更新存储的值setValue和postValue,在UI线程可以直接使用setValue,在非UI线程使用postValue;
3、扩展LiveData
前面说过LiveData可以感知声明周期,如果部分业务请求对生命周期敏感,可以扩展LiveData,根据生命周期回调做相应的工作:
class StockLiveData(symbol: String) : LiveData() {
private val stockManager = StockManager(symbol)
private val listener = { price: BigDecimal ->
value = price
}
override fun onActive() {
stockManager.requestPriceUpdates(listener)
}
override fun onInactive() {
stockManager.removeUpdates(listener)
}
}
4、LiveData转换 -- Transformations
如果想在LiveData的值返回给观察者前进行处理,可以使用Transformations类的map()和switchMap()方法,这两个方法类似,都是以原始LiveData和一个函数做入参,返回一个新的LiveData,区别是这个函数入参的返回值,一个是返回处理过后的数据,一个是返回新的LiveData,看官方文档有点难理解,但是看源码就比较清晰,他们都是返回了一个新的MediatorLiveData对象,对原始LiveData做了处理,可以看下官方例子和map()源码
// userLiveData值更新会触发userName更新,用户监听userName即可;
// 原始User对象用来生成userName的值
val userLiveData: LiveData = UserLiveData()
val userName: LiveData = Transformations.map(userLiveData) {
user -> "${user.name} ${user.lastName}"
}
//userId值更新会触发user更新,用户监听user即可;
//原始userId用来生成新的LiveData
private fun getUser(id: String): LiveData {
...
}
val userId: LiveData = ...
val user = Transformations.switchMap(userId) { id -> getUser(id) }
//map()源码
public static LiveData map(@NonNull LiveData source, @NonNull final Function mapFunction) {
final MediatorLiveData result = new MediatorLiveData();
result.addSource(source, new Observer() {
public void onChanged(@Nullable X x) {
result.setValue(mapFunction.apply(x));
}
});
return result;
}
5、LiveData合并 ---- MediatorLiveData
MediatorLiveData是LiveData的一个子类,它可以将多个LiveData合并,有任何一个LiveData变化都可以触发通知MediatorLiveData的观察者。上面的Transformations的map()和switchMap()都是使用的MediatorLiveData实现的。
var data1 = MutableLiveData()
var data2 = MutableLiveData()
var mediator1 = MediatorLiveData()
mediator1.addSource(data1) {
mediator1.value = "handled $it"
}
mediator1.addSource(data2) {
mediator1.value = "handled $it"
}
var mediator2 = MediatorLiveData().apply {
this.addSource(data1){
this.value = "handled $it"
}
this.addSource(data2){
this.value = "handled $it"
}
}