「Jetpack - LiveData解析」

「Jetpack - LiveData解析」
一、概览

LiveData-属于Android Jetpack的一部分,是一种可观察的数据存储容器类。它的强大之处在于其具有生命周期感知能力,也不会发生内存泄漏问题。当然现在如果可以还是建议转化到KotlinFlow,谷歌虽然没有明确表示放弃LiveData,但是Kotlin毕竟是趋势。

使用LiveData还是具有一些优势的:

  • LiveData具有生命周期感知能力,不需要手动管理生命周期。
  • 数据驱动模型,由数据来驱动UI的变更。
  • 生命周期自动感知,可以很好的避免内存泄漏问题。
  • 可以用来数据共享,例如同一Activity内多个Fragment共享同一个LiveData,当然这需要配合ViewModel,其实本质是ViewModel的共享。
二、使用

依赖引入

def lifecycle_version = "2.5.0-alpha01"
//LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
//ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"

ViewModel中使用LiveData,例如获取一个UserVO对象则可以:

class UserViewModel : ViewModel() {
  val userVO: MutableLiveData<UserVO> by lazy {
    MutableLiveData<UserVO>()
  }
  //Repository to get value and update
}

//在Activity中观察数据变更
class Activity : AppCompatActivity() {
  private val model: UserViewModel by viewModels()
	override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val userObserver = Observer<UserVO> { userVo ->
     	//do something
    }
    //(当然为了简洁也可以使用内部类的形式)
    model.userVO.observe(this, userObserver)
  }
}

通过更新ViewModel中的LiveData来更新UI上的展示,将数据(Data)与页面(UI)区分管理,数据驱动,层级更加清晰。但是需要注意的是,observe订阅操作是需要在主线程Main中执行的。其次,在使用过程一般的流程都是首先订阅observe当数据需要变更是调用setValue()或者postValue()来实现UI的刷新,但是如果先调用setValue,然后在注册observe,那样还能收到数据嘛?答案是可以的,先看一段测试代码:

private void testLiveData() {
  MutableLiveData<String> data = new MutableLiveData<>();
  Log.d("LiveData", "set live data value");
  data.setValue("Sai");
  new Thread(() -> {
    try {
      Log.d("LiveData", "Thread sleep start");
      Thread.sleep(3000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    Log.d("LiveData", "Thread sleep end");
    runOnUiThread(() -> data.observe(Activity.this, s -> Log.d("LiveData", "s = " + s)));
    }).start();
  }
}

打印的Log信息:

D/LiveData: set live data value
D/LiveData: Thread sleep start
D/LiveData: Thread sleep end
D/LiveData: s = Sai

setValue之后延迟了3s再执行了订阅操作,但是结果仍然是可以接收到了数据,这说明LiveData数据是带有粘性属性的,那么具体的原因只有分析了源码实现才知道具体是怎样的机制。

三、LiveData源码分析
1.基础参数

LiveData作为抽象类,其内部实现不是很复杂,一般作为开发在使用过程中都是使用其实现类MutableLiveData,实现类只对外抛出了构造方法与setValue(T value)postValue(T value),分别对应在主线程中更新数据Data和子线程中更新操作。

  • MutableLiveData源码实现
    在这里插入图片描述

  • LiveData成员参数
    在这里插入图片描述

  • LiveData成员参数比较少,需要重点关注的有START_VERSIONmVersion,这个两个版本号的对比决定了是否对数据data的分发。而mPostValueRunnable,顾名思义,我们知道可以通过postValue在子线程中更新数据,那势必会使用handler机制来切换线程,mPostValueRunnable也恰恰是这么做的。具体来一步步分析实现。

2.从setValue入手

根据上述的例子,这里第一步调用的是setValue,然后延迟3s才完成绑定的操作,但结果是依然收到了数据显示。那么这里来看看这个过程到底是如何进行数据分发的:

@MainThread
protected void setValue(T value) {
  assertMainThread("setValue");
  mVersion++;
  mData = value;
  dispatchingValue(null);
}

LiveData作为抽象类,使用的是其实现类MutableLiveData其构造函数为:

public MutableLiveData() {
  super();
}

而在LiveData中无参的构造函数为:

static final int START_VERSION = -1;
public LiveData() {
  mData = NOT_SET;
  mVersion = START_VERSION;
}

这里可以发现mVersion在构造之初的值是 -1,回到 setValue() 方法之中,mVersion进行了自增操作此时结果为:

mVersion = 0;
mData = value;

赋值操作完成以后紧接着进行数据Data的分发-通过dispatchingValue(@Nullable ObserverWrapper initiator)
在这里插入图片描述

  • 以首次set为例简化分析,此时传入参数为null也即是dispatchingValue(null),首先判断数据是否被分发过,这里显示是false,且initiator的值也为null,继而执行到for循环当中。mObservers此时的size为零(测试代码中延迟3s才执行订阅操作)。最终结果,相当于没有将数据分发出去。那么此时就需要分析订阅observe具体实现了。

  • observe方法具体实现:
    在这里插入图片描述

observe主要分为四个执行流程,具体分析之前先看看LifecycleBoundObserver是个啥:
在这里插入图片描述

LifecycleBoundObserver即是ObserverWrapper的具体实现,之前分析的dispatchingValue分发的“对象”就是ObserverWrapper。总结一下这个类有什么作用,关键注意 onStateChanged() 方法:

1.通过订阅observe将当前组件的生命周期与LifecycleBoundObserver绑定。

2.监听宿主的生命周期,根据生命周期的变化处理资源的反注册,解绑。

3.判断是否是活跃状态而决定是否进行数据的分发。

回到observe方法总结一下整个执行的流程:

1.主线程的检查,判断是否在主线程中订阅,这个是强制在主线程中执行的。

2.状态检查,如果是DESTROYED状态则直接返回。

3.通过订阅传入的上文参数owner,内部实例化一个LifecycleBoundObserver与之生命周期相绑定,并将其添加到内部维护的Map容器mObservers中。同时判断是否已经存在相同的对象,这里主要是防止同一个observer被不同的组件所持有。

4.通过addObserver操作将wrapper添加到当前组件内部,其实就是LifecycleRegistry中的具体实现。

到这里似乎还是不能解释测试代码中LiveData的“粘性”问题。接着查看LifecycleRegistryaddObserver,看看这个“之前”的数据到底是如何能够被接收到的。LifecycleRegistry#addObserver

在这里插入图片描述
注意到这个dispatchEvent()方法,其为LifecycleRegistry的内部方法,具体实现:

void dispatchEvent(LifecycleOwner owner, Event event) {
  State newState = event.getTargetState();
  mState = min(mState, newState);
  mLifecycleObserver.onStateChanged(owner, event);
  mState = newState;
}
//LifecycleEventObserver mLifecycleObserver;

回忆LiveDataLifecycleBoundObserver的具体实现,就实现了LifecycleEventObserver接口,那么调用observe绕了一圈,最终还是回到了LiveDataLifecycleBoundObserveronStateChanged方法之中:

@Override
public 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();
  }
}

本质就是对生命周期状态的监听,当宿主的生命周期已经到达末期DESTROYED则解绑此mObserver,那么最终会回调到哪个方法呢?还是走到了dispatchingValue方法中。但是区别在于此时initiator已经不为null了,即执行到considerNotify(initiator)

if (initiator != null) {
  considerNotify(initiator);
  initiator = null;
}

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);
}
//int mLastVersion = START_VERSION;

关键看这个mLastVersion,其初始值为START_VERSION = -1,而mVersionsetValue调用时作了自增操作也即是mVersion = 0,很显然mLastVersion < mVersion,往后执行数据被更新通过调用observer.mObserver.onChanged((T) mData);那么此时UI则可以根据数据来刷新。到这里为什么首先调用setValue然后observe时同样可以收到数据,也即是LiveData的粘性。总结一下(先设置数据,再订阅observe):

  • 实例化MutableLiveData对象时,将mVersion的值修改为START_VERSION,也即是 -1

  • 调用setValue时,mVersion进行了自增操作,此时mVersion的值为0,因为此时还没有订阅者订阅(observe),mDispatchingValue的值不变依然为false

  • 延迟调用observe进行订阅,内部对传入的owner的上下文进行包裹,将宿主的生命周期与LifecycleBoundObserver进行绑定操作。而LifecycleBoundObserver继承自ObserverWrapper并实现了接口LifecycleEventObserver,检查判断同一个observer是否被绑定了不同的生命周期组件;这是不允许的。最终将LifecycleBoundObserver添加到LifecycleRegistry中。

  • LifecycleRegistry中通过dispatchEvent将状态抛出,LifecycleEventObserver#onStateChanged(),而之前提到的LifecycleBoundObserver本身实现了LifecycleEventObserver接口。方法最终走到LiveData#dispatchingValue,此时initiator已经不为空了,自然就执行到了considerNotify

  • considerNotify对两个版本号mLastVersionmVersion作了对比,而经过之前的分析mLastVersion是小于mVersion的,那么最终完成了数据的分发,通知到了UI上,即是observe订阅操作是延迟后的。

3.postValue线程切换

但凡Android的现程切换,首先想到的就是利用Handler机制,那么这里的postValue是否也是一样符合猜想呢?

protected void postValue(T value) {
  boolean postTask;
  synchronized (mDataLock) {
    postTask = mPendingData == NOT_SET;
    mPendingData = value;
  }
  if (!postTask) {
    return;
  }
  ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

ArchTaskExecutor继承自抽象类TaskExecutor,方法比较简单:
在这里插入图片描述
具体实现在TaskExecutor的默认实现DefaultTaskExecutor,关键方法postToMainThread

//ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
//最终执行就是通过handler来将任务Task加入到主线程中
public void postToMainThread(Runnable runnable) {
  if (mMainHandler == null) {
     synchronized (mLock) {
        if (mMainHandler == null) {
            mMainHandler = createAsync(Looper.getMainLooper());
        }
     }
  }
  //noinspection ConstantConditions
  mMainHandler.post(runnable);
}

当调用postValue内部还是通过handler机制进行了线程的切换,最终完成数据的分发。

四、小结
  • 结合源码的实现分析,即使先调用setValue然后订阅observe,依然会收到数据;实际开发过程中要根据实际情况决定要不要使用粘性的数据。
  • 内部的版本号mVersion可以根据实际情况来区分对“事件”还是“状态”,事件一般消费完就标志着结束,而“状态”可以多次消费。
  • postValue的线程切换还是利用到了Handler机制。
  • Kotlin作为趋势,并且Flow也不存在LiveData粘性缺点,当然还是跟着管方推荐方式构建。
五、参考

LiveData

MAD Skills

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快乐二狗呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值