免费自助建站哪家好,设计师品牌 网站,重装wordpress如何删除旧数据,如何推广自己的外贸网站Android Jetpack组件架构#xff1a; LiveDate的使用和原理 导言
继Lifecycle组件之后我们接下来要介绍的就是LiveDate组件#xff0c;所谓LiveDate字面意思上就是有声明的数据#xff0c;当数据有改动时该组件可以感知到这个操作并将该事件通知到其观察者#xff0c;这样…Android Jetpack组件架构 LiveDate的使用和原理 导言
继Lifecycle组件之后我们接下来要介绍的就是LiveDate组件所谓LiveDate字面意思上就是有声明的数据当数据有改动时该组件可以感知到这个操作并将该事件通知到其观察者这样我们就可以在观察者中做出一些处理一般都是用来更新UI的操作。这样就实现了当数据改变时U界面自动更新的效果。
LiveDate的使用
LiveDate ViewModel 实现UI感知数据变化
首先我们来介绍使用LiveDate的基本姿势也就是LiveDate配合ViewModel使用这符合MVVM架构的设计至于ViewModel我们将在之后的篇章中介绍到这里先简单使用一下它。
首先我们创建一个ViewModel类
class SimpViewModel: ViewModel() {//LiveDate的实例--在ViewModel中创建val mLiveDate MutableLiveDataString()
}里面存储了一个LiveDate的实例MutableLiveData是LiveDate的实现类后面的String泛型代表该LiveDate中存储的数据是String类型的。紧接着我们在Activity中使用这里我们启用了ViewBinding进行布局的自动绑定
private const val TAG MainActivity
class MainActivity : AppCompatActivity() {//懒加载private val mBinding:ActivityMainBinding by lazy {ActivityMainBinding.inflate(layoutInflater)}private lateinit var mViewModel:SimpViewModeloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(mBinding.root)//创建出ViewModel对象mViewModel ViewModelProvider(this).get(SimpViewModel::class.java)//获得LiveDate对象val mLiveDate mViewModel.mLiveDate//观察LiveDate一旦有数据改变就触发观察方法更新UImLiveDate.observe(this,{mBinding.simpleLiveDate.text itLog.d(TAG, 在线程:${Thread.currentThread().name}中运行)})GlobalScope.launch{//延迟5sdelay(5000)mLiveDate.postValue(LifeDate已更新)}}
}可以看到我们首先是创建出了一个ViewModel对象然后获取到了它的LiveDate实例接着我们就调用到了LiveDate的observe方法该方法是用于为LiveDate对象添加一个观察者的第一个参数为LifecycleOwner对象这个我们在Lifecycle的介绍中提到过了是一个具有生命周期的对象传入的意义是什么呢这主要是为了在观察的Activity进入休眠状态时不再通知其观察者这样可以实现正确通知观察者的效果。传入的第二个对象就是一个观察者了我们需要实现其Oberserve方法这个方法就是在LiveDate的数据变化时触发的。
最后我们创建了一个协程先延时五秒钟然后通过postValue方法改变了LiveDate中的值具体我们还有一个setValue方法也可以改变LiveDate的值不同之处在于postValue方法可以在非主线程中调用而setValue方法必须在主线程中调用在这个observe方法中我们还额外打印出来这个回调方法是在哪个线程中执行的通过打印我们也可以发现是在主线程中被调用的这也在意料之中因为只能在主线程中更新UI
更改LiveDate的数据
接下来我们将在LiveDate更新完的数据传递到观察者对象之前对该数据进行更改相当于是对setValue或者postValue设置的值进行额外的处理再传递给Observe方法执行。这里我们以一个根据当前温度来判断天气的例子先在ViewModel中新建一个MapLiveDate
class SimpViewModel: ViewModel() {//LiveDate的实例--在ViewModel中创建val mLiveDate MutableLiveDataDouble()val mMapLiveDate Transformations.map(mLiveDate,{val s1 when(it) {in -20.0 .. 0.0 - 很冷in 0.0 .. 10.0 - 冷in 10.0 .. 15.0 - 较冷in 15.0 .. 28.0 - 温暖in 28.0..Double.MAX_VALUE - 热else - { 您是人吗 }}当前天气${s1}})
}这个LiveDate是通过Transformations.map方法创建出来的第一个参数就是它跟踪的LiveDate一旦它跟踪的LiveDate发生了数据更新事件该LiveDate就会将变化后的数据捕获并可以在其方法中对捕获的数据进行处理比如说在这里我们就根据传入的Double类型的参数判断天气并将其转化成String类型的参数传递给该LiveDate的Observe方法。
然后我们再更改Activity中的代码
private const val TAG MainActivity
class MainActivity : AppCompatActivity() {private val mBinding:ActivityMainBinding by lazy {ActivityMainBinding.inflate(layoutInflater)}private lateinit var mViewModel:SimpViewModeloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(mBinding.root)//创建出ViewModel对象mViewModel ViewModelProvider(this).get(SimpViewModel::class.java)//获得LiveDate对象val mLiveDate mViewModel.mLiveDateval mMapDate mViewModel.mMapLiveDate//观察LiveDate一旦有数据改变就触发观察方法更新UImLiveDate.observe(this,{
// mBinding.simpleLiveDate.text it.toString()Log.d(TAG, 更新之后的数据为:${it.toString()})Log.d(TAG, 在线程:${Thread.currentThread().name}中运行)})mMapDate.observe(this,{mBinding.simpleLiveDate.text it})GlobalScope.launch{//延迟5sdelay(3000)mLiveDate.postValue(10.0)}}
}不同之处在于这里我们是在我们新创建出来的MapLiveDate中更新UI的按照道理来说这个mMapLiveDate中收到的参数就是我们之前根据Double将其转化成String之后的结果我们来看运行结果 可以看到这里更新的UI就是我们转化之后的数据了那前一个Observer接收到的是什么参数呢 可以看到前一个Observer接收到的参数还是一样的。
合并多个LiveDate的数据源
这个所谓合并多个LiveDate实际上就是用一个大的LiveDate来监听多个其他小的LiveDate来达到任何一个小LiveDate发生改变时大的LiveDate都能监听到的效果。话不多说直接上代码viewModel
private const val TAG SimpViewModel
class SimpViewModel: ViewModel() {//LiveDate的实例--在ViewModel中创建val mLiveDate MutableLiveDataDouble()val mLiveDate2 MutableLiveDataDouble()val mMapLiveDate Transformations.map(mLiveDate,{val s1 when(it) {in -20.0 .. 0.0 - 很冷in 0.0 .. 10.0 - 冷in 10.0 .. 15.0 - 较冷in 15.0 .. 28.0 - 温暖in 28.0..Double.MAX_VALUE - 热else - { 您是人吗 }}当前天气${s1}})val mMergyLiveDate MediatorLiveDataDouble()init {mMergyLiveDate.apply {addSource(mLiveDate, {Log.d(TAG, :接收到了mLiveDate的数据${it})})addSource(mLiveDate2, {Log.d(TAG, :接收到了mLiveDate2的数据${it})})}}}Activity:
private const val TAG MainActivity
class MainActivity : AppCompatActivity() {private val mBinding:ActivityMainBinding by lazy {ActivityMainBinding.inflate(layoutInflater)}private lateinit var mViewModel:SimpViewModeloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(mBinding.root)//创建出ViewModel对象mViewModel ViewModelProvider(this).get(SimpViewModel::class.java)//获得LiveDate对象val mLiveDate mViewModel.mLiveDateval mLiveData2 mViewModel.mLiveDate2val mMapDate mViewModel.mMapLiveDateval mMergyLiveDate mViewModel.mMergyLiveDate//观察LiveDate一旦有数据改变就触发观察方法更新UImLiveDate.observe(this,{Log.d(TAG, LiveDate1 更新之后的数据为:${it.toString()})Log.d(TAG, 在线程:${Thread.currentThread().name}中运行)})mLiveData2.observe(this,{Log.d(TAG, LiveDate2 更新之后的数据为:${it.toString()})})mMapDate.observe(this,{mBinding.simpleLiveDate.text it})mMergyLiveDate.observe(this,{Log.d(TAG, 接收到的数据为:${it})})GlobalScope.launch{//延迟5smLiveDate.postValue(10.0)delay(3000)mLiveData2.postValue(15.0)}}
}可以在打印日志中看到这两个LiveDate发生改变时MergyLiveDate都感受到了 以上就是所有有关LiveDate的基础使用了接下来我们来分析LiveDate的原理。
LiveDate的原理
首先我们直接点开源文件它的开头也有一大段注释 这大段的注释中有几个比较重要的信息首先是Observer观察者只有在处于Active状态时才能感受到LiveDate中数据的变化至于这个Active状态是在Lifecycle组件的状态处于Lifecycle.State.STARTED或者Lifecycle.State.RESUMED时Observer才处于 Active状态这也就是我们一开始说的LiveDate是依赖于Lifecycle组件框架的原因。当宿主Lifecycle的状态处于Lifecycle.State.DESTROYED 时Observer将会被自动移除这个功能显然可以避免内存泄漏的问题。
Observe开始观察
首先我们来分析Observe方法该方法的作用就是为LiveDate设置一个观察者 MainThreadpublic void observe(NonNull LifecycleOwner owner, NonNull Observer? super T observer) {assertMainThread(observe);//判断是否运行在主线程中if (owner.getLifecycle().getCurrentState() DESTROYED) {//如果宿主Lifecycle当前的状态为DESTROYED// ignorereturn;//说明当前LiveDate不是处于Active状态不进行任何处理直接返回}LifecycleBoundObserver wrapper new LifecycleBoundObserver(owner, observer); //将宿主Owner和观察者Observer包装成一个装饰类ObserverWrapper existing mObservers.putIfAbsent(observer, wrapper); //判断LiveDate中是否已经有Observerif (existing ! null !existing.isAttachedTo(owner)) { //如果LiveDate中的Observer列表中已经存在了throw new IllegalArgumentException(Cannot add the same observer with different lifecycles); //报错一个Observer不能多次观察一个LiveDate}if (existing ! null) { return;}//如果原有的列表中不存在的话说明之前没有注册过将其添加进Observers列表中owner.getLifecycle().addObserver(wrapper);}在上面的方法中已经对大概的逻辑进行了一些注释了首先这个方法是需要在主线程执行的不然就会抛出异常。之后会首先判断当前LiveDate是否处于Active状态如果不是Active状态那么就直接返回此时不需要进行数据的观察。接下来会将LifecycleOwner和传入的Observer包装成一个LifecycleBoundObserver对象关于这个类我们等等再来细看。之后调用到mObservers.putIfAbsent方法mObservers是一个可以安全迭代即可以在迭代时安全修改的Map private SafeIterableMapObserver? super T, ObserverWrapper mObservers new SafeIterableMap();至于putIfAbsent方法和一般的put方法有一些差别如果传入的key对应的value已经存在那么将不会进行替换并且返回已经存在的value如果传入的key对应的value不存在的话则会将key和value添加进入Map中并且返回null.
所以这一个方法完成的是两个目的首先是在Map中添加元素其次是判断传入的key在之前是否已经被添加过了如果LiveDate中的Observer列表中已经存在了就直接抛出异常因为一个Observer不允许被多次添加进入一个LiveDate的观察者列表中。如果之前不存在的话则会在最后调用addObserver方法将其添加进入Observer列表中不过这里我们可以发现这里将其添加进入的是Lifecycle的列表而不是LiveDate的列表LiveDate的列表在之前的putIfAbsent方法中已经添加了。
LifecycleBoundObserver类 和 ObserverWrapper类
我们先来看LifecycleBoundObserver这个类的整个结构 class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {NonNullfinal LifecycleOwner mOwner;LifecycleBoundObserver(NonNull LifecycleOwner owner, Observer? super T observer) {super(observer);mOwner owner;}Overrideboolean shouldBeActive() {return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);}Overridepublic void onStateChanged(NonNull LifecycleOwner source,NonNull Lifecycle.Event event) {Lifecycle.State currentState mOwner.getLifecycle().getCurrentState();if (currentState DESTROYED) {removeObserver(mObserver);return;}Lifecycle.State prevState null;while (prevState ! currentState) {prevState currentState;activeStateChanged(shouldBeActive());currentState mOwner.getLifecycle().getCurrentState();}}Overrideboolean isAttachedTo(LifecycleOwner owner) {return mOwner owner;}Overridevoid detachObserver() {mOwner.getLifecycle().removeObserver(this);}}首先可以看到它实现的是LifecycleEventObserver 接口还记得这个接口吗在上一篇关于Lifecycle的文章中提到过 既然已经知道了是LifecycleEventObserver接口的话那么其实我们就可以很清楚的知道这个Observe事件并不是由LiveDate自身分发的它的分发是和Lifecycle中的默认观察者一样的也就是说是通过Lifecycle的Observer列表进行分发的。所以我们接下来直接看它的onStateChanged方法就好了这个方法是在Lifecycle进行事件分发时触发的。 Overridepublic void onStateChanged(NonNull LifecycleOwner source,NonNull Lifecycle.Event event) {Lifecycle.State currentState mOwner.getLifecycle().getCurrentState();//获得当前宿主的生命周期状态if (currentState DESTROYED) { //如果宿主已经消失那么将其从Lifecycle的观察列表中移除removeObserver(mObserver);return;}Lifecycle.State prevState null; //之前的状态while (prevState ! currentState) { //当之前的状态 不等于 当前状态prevState currentState; //将状态迭代//这里调用activeStateChanged方法activeStateChanged(shouldBeActive());//shouldBeActive方法获得的是当前状态是否应该是Active状态//更新当前状态currentState mOwner.getLifecycle().getCurrentState();}}具体的逻辑也已经在上面标注出来了唯一的问题就是activeStateChanged方法我们来看这个方法这个方法是在LiveDate的内部抽象类ObserverWrapper中定义的他也是上面的LifecycleBoundObserver的父类所以接下来又要看ObserverWrapper类了 private abstract class ObserverWrapper {final Observer? super T mObserver;boolean mActive;int mLastVersion START_VERSION;ObserverWrapper(Observer? super T observer) {mObserver observer;}abstract boolean shouldBeActive();boolean isAttachedTo(LifecycleOwner owner) {return false;}void detachObserver() {}void activeStateChanged(boolean newActive) {if (newActive mActive) {return;}// immediately set active state, so wed never dispatch anything to inactive// ownermActive newActive;changeActiveCounter(mActive ? 1 : -1);if (mActive) {dispatchingValue(this);}}}这个类也是和它的名字相符合整个一个就是对Observer进行了装饰所以整个装饰下来就是这样的 我们的Observer经过重重装饰就已经实现了LifecycleEventObserver接口从而其方法也可以由整个Lifecycle框架进行分发了。回到正题来看它的activeStateChanged方法如果Active状态并没有发生改变的话就直接返回不作任何处理。否则他会调用到changeActiveCounter方法并且如果当前新状态为Active的话还会进行值的分发。
LiveDate更改观察者状态
前面提到了会调用changeActiveCounter方法这个方法是在LiveDate中定义的具体如下 void changeActiveCounter(int change) {int previousActiveCount mActiveCount; //获取之前的ActiveCount的值mActiveCount change; //更新ActiveCount的值if (mChangingActiveState) { //如果当前有线程正在处理的话就直接返回return;}mChangingActiveState true;try {while (previousActiveCount ! mActiveCount) { //当之前的ActiveCount的值与现在的ActiveCount的值不等的话boolean needToCallActive previousActiveCount 0 mActiveCount 0;//如果之前的ActiveCount为0且现在的ActiveCount值大于0boolean needToCallInactive previousActiveCount 0 mActiveCount 0;//如果之前的ActiveCount大于0且当前的ActiveCount值等于0previousActiveCount mActiveCount;//更新之前的ActiveCount的值if (needToCallActive) {//如果需要调用onActive方法的话onActive();} else if (needToCallInactive) {//如果需要调用onInActive方法的话onInactive();}}} finally {mChangingActiveState false;}}该方法做的事情主要就是维护mActiveCount的值这个mActiveCount就是有多少个之前处于Active状态下的Observer的数量如果观察该LiveDate的观察者全都不是处于Active状态的话就调用onInactive()回调并且此时逻辑上LiveDate是处于非Active状态的反之就会回调onActive方法这个方法默认是没有实现的。
LiveDate分发Value
接下来我们再来看dispatchingValue方法 void dispatchingValue(Nullable ObserverWrapper initiator) {if (mDispatchingValue) {mDispatchInvalidated true;return;}mDispatchingValue true;do {mDispatchInvalidated false;if (initiator ! null) {considerNotify(initiator);initiator null;} else {for (IteratorMap.EntryObserver? super T, ObserverWrapper iterator mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {considerNotify(iterator.next().getValue());if (mDispatchInvalidated) {break;}}}} while (mDispatchInvalidated);mDispatchingValue false;}可以看到这个方法一开始的处理也是和之前的changeActiveCounter方法类似主要是为了防止并发修改。接下来的核心逻辑其实就是considerNotify方法 private void considerNotify(ObserverWrapper observer) {if (!observer.mActive) { //如果当前传入的Observer不是Active状态就直接返回return;}if (!observer.shouldBeActive()) {//如果传入的observer不应该处于Active状态也就是说当前是Active状态但是之后不应该处于Active状态observer.activeStateChanged(false);//更新Observer的active状态return;}if (observer.mLastVersion mVersion) {//如果observer的上一个Version值大于当前的Version值return; //直接返回}observer.mLastVersion mVersion; //更新Version值observer.mObserver.onChanged((T) mData); //触发回调方法}整个逻辑也很清楚首先会更新当前的Active状态之后根据当前的状态和接下来的状态(此处的状态特指Active状态)来进行Observer标志位的设置这里还涉及到一个Version值的概念我们之后再提。最后一行就是触发我们写入的onChanged方法了也就是我们传入的回调方法。不过这里只是触发我们回调的一种情况就是宿主的生命周期变化时会将触发回调我们先来简单总结一下
通过set/postValue方法触发回调
接下来是我们最重要的一个部分那就是通过setValue或者postValue方法触发我们的回调这次我们先来看postValue方法 protected void postValue(T value) {boolean postTask;//同步方法进行上锁synchronized (mDataLock) { //pendingData意思就是尚未被发送的数据postTask mPendingData NOT_SET; //此处判断本次投递是不是挂起的任务第一次总是truemPendingData value;//设置挂起数据为当前数据}//将锁释放if (!postTask) { //如果不是挂起任务的话也就是当上次任务还没有投递完成时return; //直接返回}ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);}这里涉及到了mPendingDate这个成员变量我们会在后面比较setValue方法中再次提到这里会首先通过线程池的postToMainThread方法投递mPostValueRunnable的任务这个任务是在LiveData创建的 private final Runnable mPostValueRunnable new Runnable() {SuppressWarnings(unchecked)Overridepublic void run() {Object newValue;synchronized (mDataLock) {//上锁newValue mPendingData;//将新值设置为mPendingData,这个值在之前的postValue方法中设置mPendingData NOT_SET;//重新将Pending位设置为NOT_SET}setValue((T) newValue);//调用setValue方法}};至于postToMainThread方法就是将runnable通过Handler机制投递到主线程中执行然后调用其setValue方法修改Value的值。我们可以设想一下当只有一条线程执行一次postValue的情况首先会将mDataLock进行上锁然后通过Handler将任务发送到主线程执行这个runnable运行时也会上锁然后将新值取出将mPendingData转为NOT_SET状态。最后执行setValue方法更改新值。
在多线程的情况下我们再来分析当有两条线程同时执行postValue方法时首先先进入的那条线程会将设置postTask 时的代码块先进行上锁这样第二条线程只能阻塞在代码块之前然后第一条线程执行完值的设置时将锁给释放然后第二条线程就进入了值的设置的阶段这个时候由于锁被第二条线程给持有了所以第一条线程在执行runnable时就会被阻塞第二条线程设置完值之后再将锁给释放这样第一条线程就执行了runnable方法。不过这个时候第一条线程设置的值实际上是第二条线程中传入的值,因为这个mPendingData是volatile状态的
volatile Object mPendingData NOT_SET;总之postValue方法保证了在多线程的情况下总是只有一个最新的值被执行具体的setValue操作这样做的好处就是可以提高性能且不会在主线程的消息队列中堆积过多任务导致主线程的阻塞。好了看完了postValue方法之后我们再来看setValue方法 protected void setValue(T value) {assertMainThread(setValue);mVersion;mData value;dispatchingValue(null);}首先这个方法也是一个只能在主线程调用的方法其次它维护了一个记录值变化的记录值mVersion也就是说每次成功对LiveData中的数据进行修改后mVersion的值都会发生变化以此来判断是否需要分发回调事件最后触发dispatchingValue方法这个方法我们在之前生命周期发生改变时的情况下提到过不同的在于此处传入的是null: void dispatchingValue(Nullable ObserverWrapper initiator) {if (mDispatchingValue) {mDispatchInvalidated true;return;}mDispatchingValue true;do {mDispatchInvalidated false;if (initiator ! null) {considerNotify(initiator);initiator null;} else {for (IteratorMap.EntryObserver? super T, ObserverWrapper iterator mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {considerNotify(iterator.next().getValue());if (mDispatchInvalidated) {break;}}}} while (mDispatchInvalidated);mDispatchingValue false;}具体来说会进入到后面那段for循环中
for (IteratorMap.EntryObserver? super T, ObserverWrapper iterator mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {considerNotify(iterator.next().getValue());if (mDispatchInvalidated) {break;}
}很显然是对每个观察者都调用了considerNotify方法这个方法我们之前也提到过这次我们来完全解析一下 private void considerNotify(ObserverWrapper observer) {if (!observer.mActive) {return;}if (!observer.shouldBeActive()) {observer.activeStateChanged(false);return;}if (observer.mLastVersion mVersion) {return;}observer.mLastVersion mVersion;observer.mObserver.onChanged((T) mData);}这里判断观察者是否需要执行回调就是通过开始的三个if条件来判断当Observer满足以下三个条件之一时将不会执行回调
Observer不处于Active状态Observer不应该处于Activie状态也就是说当前是Active状态但马上就不是Active状态了上一次的Observer的Version值大于等于当前的Version值正常修改之后Version值应该会变大
当不满足上面三个条件时才会执行回调方法至于Observer状态的设置是由Lifecycle进行管理的。不过这里思考一个问题当一个LiveData先执行改变Value然后一个Observer再观察时也是满足执行回调方法的情况的也就是说LiveData默认是实现了粘性事件的如果我们不想要粘性事件的话思考一下该如何做。我们可以在每次observer进入到Active状态时的mLastVersion为绑定的LiveData的mVersion值。
事件分发的总结
老规矩还是以一张图总结setValue或者postValue方法