写点什么

Android 架构之 LiveData 组件,原理竟然是这

用户头像
Android架构
关注
发布于: 刚刚

timer = new Timer();


TimerTask timerTask = new TimerTask() {


@Override


public void run() {


if(currentSecond!=null){


currentSecond.postValue(current++);


}


}


};


timer.schedule(timerTask,1000,1000);


}


}


//关闭定时器


public void stopTiming(){


timer.cancel();


}


}


1234567891011121314151617181920212223242526272829303132333435363738394041


当开始定时器的时候,也就是我们数据资源发生变化的时候,我们需要调用 livedata.postvalue 方法,通知页面我们数据源已经发生了改变。至于为什么不用 livedata.setValue 方法,等下我们会说到。


2.接着我们在 Activity 中创建 ViewModel,并监听 ViewModel 里面 currentSecond 数据的变化。


<pre language="javascript" code_block="true">public class LiveDataActivity extends AppCompatActivity {


@Override


protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);


setContentView(R.layout.activity_live_data);


iniComponent();


}


private void iniComponent() {


//通过 ViewModelProvider 得到 ViewModel


final LiveDataViewModel viewModel = new ViewModelProvider(this).get(LiveDataViewModel.class);


//得到 ViewModel 中的 LiveData


final MutableLiveData<Integer> liveData = (MutableLiveData<Integer>) viewModel.getCurrentSecond();


//通过 liveData.observer()观察 ViewModel 中数据的变化


liveData.observe(this, new Observer<Integer>() {


@Override


public void onChanged(Integer integer) {


//收到回调后更新 UI 界面


TextView tv = findViewById(R.id.tv_texts);


tv.setText("小鑫啊"+integer);


}


});


//关闭定时器


findViewById(R.id.btnReset).setOnClickListener(new View.OnClickListener() {


@Override


public void onClick(View v) {


//通过 LiveData.setValue()/LiveData.postValue()


//完成对 ViewModel 中数据的更新


liveData.setValue(0);


//关闭定时器


viewModel.stopTiming();


}


});


//计时开始


viewModel.startTiming();


}


}


1234567891011121314151617181920212223242526272829303132333435363738394041424344


在页面中,通过 LiveData.observe()方法对 LivaData 所包装的数据进行观察。当我们数据源发生变化了(也就是我们想修改 LivaData 所包装的数据时),就可以通过 LiveData.postValue/LiveData.setValue()来完成,然后 onChanged 方法就会收到我们修改之后的数据,我们就可以对 UI 进行更改了.


需要注意的是:postValue()方法用在非 UI 线程中,而 setValue()方法用在 UI 线程中,这就是为什么我们在开始定时器的时候,需要调用 postVaule()发送数据了(因为定时器是运行在非 UI 线程的).



运行结果如下:



LivaData 的基本使用就到这里,是不是很简单啊! 接下来,就让我们来探讨下 LiveData 的原理吧!!!


LiveData 的原理


======================================================================


我们知道 LiveData 是通过观察者模式实现的。当数据发送改变的时候,会回调 Observer 的 onChanged(),接下来就让我们深入 Observer 方法的源码一探究竟


observe 源码


====================================================================


<pre language="javascript" code_block="true"> @MainThread


public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {


assertMainThread("observe");


if (owner.getLifecycle().getCurrentState() == DESTROYED) {


// ignore


return;


}


LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);


//判断当前 wapper 已经添加过,如果添加过就直接返回,否则返回 null


ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);


if (existing != null && !existing.isAttachedTo(owner)) {


throw new IllegalArgumentException("Cannot add the same observer"


  • " with different lifecycles");


}


//如果已经添加过,就直接返回


if (existing != null) {


return;


}


//没有添加过,则添加 wrapper


owner.getLifecycle().addObserver(wrapper);


}


123456789101112131415161718192021


从源码可以看出,Observer()方法接收的第一个参数是一个 LifecleOwner 对象,我们传入的是 this,因为 this 的祖父类实现了这个接口,也正是 LifecleOwner 对象,LiveData 才会具体生命周期感知能力。


首先, 通过 owner.getLifecycle().getCurrentState()获取当前页面的状态,如果当前页面被销毁了,就直接返回,也就是说 LiveData 会自动清除与页面的关联。


LifecycleBoundObserver 源码


====================================================================================


<pre language="javascript" code_block="true"> class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {


@NonNull


final LifecycleOwner mOwner;


LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {


super(observer);


mOwner = owner;


}


@Override


boolean shouldBeActive() {


return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);


}


@Override


public void onStateChanged(@NonNull LifecycleOwner source,


@NonNull Lifecycle.Event event) {


if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {


removeObserver(mObserver);


return;


}


activeStateChanged(shouldBeActive());


}


1234567891011121314151617181920212223


当调用 LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer),本质是通过 ObserverWrapper 将 observer 包装起来,得以 LiveData 能对生命周期状态得以进行监听,是通过 onStateChanged 和 shouldBeActive 方法


  1. shouldBeActive 这里调用 LiftCycle 的方法,表达如果当前生命周期的状态为 onStart,onResume,onPause 时 返回 true,也就是说只有这三个状态可以接收数据更新。

  2. onStateChanged 是 LifecycleEventObserver 接口的方法,当生命周期发送变化的时候会回调它,如果当前生命周期状态是 destory,就会直接移除观察者,否则就会调用 activeStateChanged(shouldBeActive());方法激活观察者.


方法中的最后一行代码将 observer 与 Activity 的生命周期关联在一起。因此,LivaData 能够感知页面的生命周期。


observer 方法小结


=======================================================================


  1. 判断是否已经销毁,如果当前页面销毁,LiveData 自动清除与页面的关联

  2. 用 LifecycleBoundObserver 对 observer 进行一个包装

  3. 判断当前 observer 是否已经添加过,添加过就直接返回

  4. 将 observer 方法与 Activity 的生命周期进行关联


setValue 方法


=====================================================================


<pre language="javascript" code_block="true"> @MainThread


protected void setValue(T value) {


assertMainThread("setValue");


mVersion++;


mData = value;


dispatchingValue(null);


}


1234567


setValue()中,首先 断言是主线程,这里的关键是 dispatchingValue(null)方法


<pre language="javascript" code_block="true">void dispatchingValue(@Nullabl


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


e ObserverWrapper initiator) {


if (mDispatchingValue) {


mDispatchInvalidated = true;


return;


}


mDispatchingValue = true;


do {


mDispatchInvalidated = false;


if (initiator != null) {


considerNotify(initiator);


initiator = null;


} else {


for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =


mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {


considerNotify(iterator.next().getValue());


if (mDispatchInvalidated) {


break;


}


}


}


} while (mDispatchInvalidated);


mDispatchingValue = false;


}


1234567891011121314151617181920212223


只有处于 active(激活)状态的观察者,这个方法就会把数据发送给它们。由于每次 dispathchingValue 传入的 null,所以会走 else 这一部分代码, 这时候就会遍历所有的 observer,最后通过调用 considerNotify()将数据进行分发给所有的 observer


<pre language="javascript" code_block="true">private void considerNotify(ObserverWrapper observer) {


if (!observer.mActive) {


return;


}


// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.


//


// we still first check observer.active to keep it as the entrance for events. So even if


// the observer moved to an active state, if we've not received that event, we better not


// notify for a more predictable notification order.


//如果当前 observer 不是激活状态,也就是当前页面被 destory,直接 return.


if (!observer.shouldBeActive()) {


observer.activeStateChanged(false);


return;


}


if (observer.mLastVersion >= mVersion) {


return;


}


observer.mLastVersion = mVersion;


observer.mObserver.onChanged((T) mData);


}


1234567891011121314151617181920


只有出于活跃状态且数据是数据是最新的,才会去分发数据,最后回调到我们熟悉的 onChanged()方法。


postValue 方法


======================================================================


<pre language="javascript" code_block="true"> protected void postValue(T value) {


boolean postTask;


synchronized (mDataLock) {


postTask = mPendingData == NOT_SET;


mPendingData = value;


}


if (!postTask) {


return;


}


ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);


}


1234567891011


postValue 方法是可以在子线程(非 UI 线程)发送数据的,但是 onChanged()方法始终是在主线程? 答案就在 postToMainThread(mPostValueRunnable)方法中;


<pre language="javascript" code_block="true">private final Runnable mPostValueRunnable = new Runnable() {


@SuppressWarnings("unchecked")


@Override


public void run() {


Object newValue;


synchronized (mDataLock) {


newValue = mPendingData;


mPendingData = NOT_SET;


}


setValue((T) newValue);


}


};


123456789101112


创建一个 Handler 将子线程中的任务发送到主线程去执行,其本质还是调用了 setValue()方法


LiveData.observeForever()方法


======================================================================================


如果你想无论页面处于何种生命周期,setValue/postValue 之后立刻回到数据。那么可以使用 observerForever()方法,使用起来与 observer()没有太大差别. 因为 AlwaysActiveObserver 没有实现 GenericLifecycleObserver 接口,不能感应生命周期。


但是需要注意的是,在用完之后,一定要记得在 onDestroy()方法中调用 removeObserver()方法来停止对 LiveData 的观察,否则 LiveData 会一直处于激活状态,Activity 则永远不会被系统自动回收,会造成内存泄露。


ViewModel+LiveData 实现 Fragment 间的通信


===========================================================================================


我们已经知道,ViewModel 能够将数据从 Activity 中剥离出来。只要 Activity 不被销毁,ViewModel 会一直存储,并且独立于 Activity 的配置变化。


Fragment 可以被看作 Activty 的子页面,即一个 Activity 中可以包含多个 Fragment.这些 Fragment 彼此独立,但是又都属于同一个 Activity.


基于 ViewModel 和 Fragment 组件的这些特性,我们可以利用 LiveData,实现同一个 Activity 中的不同 Fragment 间的通信,因为不同的 Fragment 得到的都是同一个 LiveData;



定义 ViewModel 和 LiveData


===============================================================================


<pre language="javascript" code_block="true">public class SharedViewModel extends ViewModel {


private MutableLiveData<String> content;


@Override


protected void onCleared() {


super.onCleared();


//释放资源


content= null;


}


public LiveData<String> getContent(){


if(content == null){


content = new MutableLiveData<>();


}


return content;


}


}


1234567891011121314151617


初始化 Fragment


======================================================================


Fragment 之间的跳转我们使用导航图来进行跳转


share_graph.xml


===========================================================================


<pre language="javascript" code_block="true"><?xml version="1.0" encoding="utf-8"?>


<navigation xmlns:android="http://schemas.android.com/apk/res/android"


xmlns:app="http://schemas.android.com/apk/res-auto"


xmlns:tools="http://schemas.android.com/tools"


android:id="@+id/share_graph"

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android架构之LiveData组件,原理竟然是这