Android架构组件之ViewModel和LiveData

关于应用架构,Google官方现在主推MVVM架构,官方推出的JetPack库提供了一系类支持MVVM架构,其中最核心的两个类是ViewModel和LiveData。

在MVVM架构中,View通常指Activity和Fragment,主要用来根据数据渲染UI,而Model主要负责数据的获取,这里通常包含获取网络数据和本地缓存数据,而ViewModel作为View和Model的桥梁,主要负责UI数据的处理,官方提供了ViewModel类作为一种实现。LiveData作为一种可观察的数据存储类,可以很好在支持ViewModel处理好的数据通知UI的更新。

ViewModel

主要负责存储和管理和界面相关的数据。很多地方都提到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;
        }
    }

LiveData

1、简介  

看官方文档我们知道LiveData是一种可观察的数据存储类,它具有感知生命周期的能力。通常ViewModel是靠LiveData通知View层数据变化的,Activity、Fragment和Service监听LiveData,当数据变化时会收到通知做相应的UI更新工作。上面说了LiveData有生命周期感知能力,所以当Activity处于非活跃状态时,是不会收到通知的,当Activity再次处于活跃状态时,会重新收到数据变化的通知。我们可以看下典型的使用的方式:

       // void observe(LifecycleOwner owner, Observer 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公开了两个方法用来更新存储的值setValuepostValue,在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"
            }
        }

你可能感兴趣的:(Android开发,kotlin,ViewModel,LiveData)