Android 架构之 LiveData 组件,原理竟然是这
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 是通过观察者模式实现的。当数据发送改变的时候,会回调 Observer 的 onChanged(),接下来就让我们深入 Observer 方法的源码一探究竟
====================================================================
<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 会自动清除与页面的关联。
====================================================================================
<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 方法
shouldBeActive 这里调用 LiftCycle 的方法,表达如果当前生命周期的状态为 onStart,onResume,onPause 时 返回 true,也就是说只有这三个状态可以接收数据更新。
onStateChanged 是 LifecycleEventObserver 接口的方法,当生命周期发送变化的时候会回调它,如果当前生命周期状态是 destory,就会直接移除观察者,否则就会调用 activeStateChanged(shouldBeActive());方法激活观察者.
方法中的最后一行代码将 observer 与 Activity 的生命周期关联在一起。因此,LivaData 能够感知页面的生命周期。
=======================================================================
判断是否已经销毁,如果当前页面销毁,LiveData 自动清除与页面的关联
用 LifecycleBoundObserver 对 observer 进行一个包装
判断当前 observer 是否已经添加过,添加过就直接返回
将 observer 方法与 Activity 的生命周期进行关联
=====================================================================
<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
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()方法。
======================================================================
<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()方法
======================================================================================
如果你想无论页面处于何种生命周期,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;
===============================================================================
<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 之间的跳转我们使用导航图来进行跳转
===========================================================================
<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"
评论