<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
本文來分析下 LiveData 的原始碼,以及其在實際開發中的一些問題。
一般來說 LiveData 都會配合 ViewModel 使用,篇幅原因關於 ViewModel 的內容將在後續部落格中分析,目前可以將 ViewModel 理解為一個生命週期比 Activity 更長的物件,且不會造成記憶體漏失。
範例程式碼:
MainViewModel.kt
class MainViewModel: ViewModel() { // 定義 LiveData 注意這裡給了 0 作為初始值 val number = MutableLiveData<Int>(0) fun add(){ // 相當於 number.setValue(number.getValue() + 1) number.value = number.value?.plus(1) } fun sub(){ // 相當於 number.setValue(number.getValue() - 1) number.value = number.value?.minus(1) } }
MainACtivity.kt
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 獲取 ViewModel 範例 val vm = ViewModelProvider(this).get(MainViewModel::class.java) // 呼叫 ViewModel 方法 進行加法操作 bnAdd.setOnClickListener { vm.add() } // 呼叫 ViewModel 方法 進行減法操作 bnSub.setOnClickListener { vm.sub() } // 觀察 LiveData 變化, Observer 是介面,kotlin 寫法簡化 vm.number.observe(this, Observer { tvNumber.text = it.toString() }) } }
XML 非常簡單就不貼了,看下效果圖:
很簡單的功能,但是有兩個問題需要注意:
本文將以這兩個問題作為切入點,對 LiveData 原始碼進行分析。
從範例程式碼中很容易看出這是典型的觀察者模式,當 LiveData 發生變化時會對其訂閱者傳送通知,將最新值傳遞過去,Observer 就相當於其觀察者,先來看一下 Observer 介面:
public interface Observer<T> { void onChanged(T t); }
當 LiveData 發生變化時,就會觸發其觀察者的 onChanged 方法,並傳遞最新值;
再看一下其新增訂閱時的原始碼:
public abstract class LiveData<T> { //... public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) { // 檢查是否在主執行緒 assertMainThread("observe"); // 如果觀察者所在元件的生命週期為 DESTROYED 則直接 return if (owner.getLifecycle().getCurrentState() == DESTROYED) { return; } // LifecycleBoundObserver 實現了 ObserverWrapper // 理解為這是對 觀察者 Observer 的一層包裝類即可 LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); // mObservers 是一個 Map 容器,原始的 Observer 為 key,包裝後的 wrapper 為 value ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); // 同一個 observer 不能在不同的生命週期元件中進行訂閱 if (existing != null && !existing.isAttachedTo(owner)) { throw new IllegalArgumentException("Cannot add the same observer" + " with different lifecycles"); } // 重複訂閱直接return if (existing != null) { return; } // LifecycleBoundObserver 利用 Lifecycle 實現自動解綁 // Lifecycle 原理詳見我之前的部落格 owner.getLifecycle().addObserver(wrapper); } // ... }
從原始碼中得知訂閱必須在主執行緒(這一點也非常適用於 Android 的 UI 更新), 訂閱後會放入一個 Map 容器中儲存;
接著來看一下 LiveData 是如何對 Observer 進行包裝的,LifecycleBoundObserver 實現了 ObserverWrapper,那麼就先來看看 ObserverWrapper 的原始碼:
private abstract class ObserverWrapper { final Observer<? super T> mObserver; // Observer 原始物件 boolean mActive; // 是否啟用 int mLastVersion = START_VERSION; // 版本號 預設 -1 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; } mActive = newActive; // 不一樣則更新 mActive changeActiveCounter(mActive ? 1 : -1); // 記錄有多少個啟用狀態的observer // 注意這裡,如果mActive是從false變更為true 則呼叫一次 dispatchingValue // dispatchingValue 的原始碼下面再分析 if (mActive) { dispatchingValue(this); } } }
接著看一下 LifecycleBoundObserver 的原始碼:
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); } // 如果再 activity 中進行 observer // 當 activity 生命週期發生變化時 會回撥到這裡 @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState(); // 自動解綁 if (currentState == DESTROYED) { // removeObserver 內部會將 observer 從 map 容器中移除 // 並且呼叫其 detachObserver 方法 removeObserver(mObserver); return; } Lifecycle.State prevState = null; while (prevState != currentState) { prevState = currentState; // activeStateChanged 上面已經說過了 // 如果 mActive 由 fasle 變更為 true 會執行一次 dispatchingValue activeStateChanged(shouldBeActive()); currentState = mOwner.getLifecycle().getCurrentState(); } } // ... }
上述的觀察者相關的重要原始碼已經分析完,接著來看一下範例程式碼中定義的 MutableLiveData 原始碼:
public class MutableLiveData<T> extends LiveData<T> { public MutableLiveData(T value) { super(value); } public MutableLiveData() { super(); } @Override public void postValue(T value) { super.postValue(value); } @Override public void setValue(T value) { super.setValue(value); } }
繼承自 LiveData,作用很明顯暴露出其 postValue、setValue 方法,那麼就先來看一下這兩個方法呼叫邏輯
先來看看 postValue:
public abstract class LiveData<T> { protected void postValue(T value) { boolean postTask; synchronized (mDataLock) { // mPendingData 預設值為 NOT_SET postTask = mPendingData == NOT_SET; // 呼叫 postValue 後,會賦值成傳進來的 value mPendingData = value; } if (!postTask) { // 第一次呼叫 肯定為 true return; } // 核心在於這一行,postToMainThread // 看名字也知道是切換到主執行緒去執行 mPostValueRunnable ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable); } }
ArchTaskExecutor.getInstance() 會初始化其內部的 mDelegate 變數,其最終實現是 DefaultTaskExecutor;DefaultTaskExecutor 內部包含一個主執行緒 Handler,其 postToMainThread 方法就是利用 Handler 將 runnable 傳送至主執行緒執行。這裡面的原始碼比較簡單,就不貼出來細節了,看一下 mPostValueRunnable 具體執行了什麼:
private final Runnable mPostValueRunnable = new Runnable() { @Override public void run() { Object newValue; synchronized (mDataLock) { // 加鎖同步 newValue = mPendingData; // 獲取最新傳遞過來的值 mPendingData = NOT_SET; // 將 mPendingData 恢復為預設值 } // 最終還是呼叫了 setValue setValue((T) newValue); } };
可以看出 postValue 可以在任意執行緒呼叫,最終都會被切換到主執行緒呼叫 setValue,但是需要注意,頻繁呼叫 postValue 可能會只保留最後一次的值,因為每次 postValue 會導致 mPendingData 設定為新的值,但如果多次 postValue 在子執行緒執行,但是主執行緒還沒有來得及執行 mPostValueRunnable,會導致 mPendingData 沒有被恢復為 NOT_SET,那麼 postTask 即為 false,但 mPendingData 會設定為最新值,當 mPostValueRunnable 執行時從 mPendingData 中獲取的也是最新值。
postValue 內部最終呼叫了 setValue,那麼就來看看 setValue 的原始碼:
public abstract class LiveData<T> { static final int START_VERSION = -1; private volatile Object mData; private int mVersion // 帶初始值的構造器 public LiveData(T value) { mData = value; // 直接給 mData 賦值 mVersion = START_VERSION + 1; //版本號 +1,也就是 0 } // 無參構造器 public LiveData() { mData = NOT_SET; mVersion = START_VERSION; // 版本號預設 -1 } protected void setValue(T value) { // 內部根據 Looper 判斷是否在主執行緒,不在主執行緒則丟擲異常 assertMainThread("setValue"); // 版本號 +1 mVersion++; // LiveData 的資料,也就是被觀察的資料,設定為最新值 mData = value; // 這裡是重點 dispatchingValue(null); } }
從原始碼中得知,setValue 只能從主執行緒呼叫,內部對版本號進行++操作,並且設定 mData 為最新值,最終呼叫 dispatchingValue:
// 用於儲存其觀察者 Observer,Observer 會包裝成 private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers = new SafeIterableMap<>(); void dispatchingValue(@Nullable ObserverWrapper initiator) { if (mDispatchingValue) { // 預設為 false mDispatchInvalidated = true; return; } mDispatchingValue = true; // 進入方法後設定為 true do { mDispatchInvalidated = false; // setValue 傳進來的是 null 不會進入這個 if // initiator 實際上就是觀察者,如果傳遞進來一個觀察者物件 // 則只進行一次 considerNotify 方法呼叫 if (initiator != null) { considerNotify(initiator); initiator = null; } else { // 遍歷自身的觀察者 for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext(); ) { // 呼叫 considerNotify 將觀察者傳入 considerNotify(iterator.next().getValue()); if (mDispatchInvalidated) { break; } } } } while (mDispatchInvalidated); mDispatchingValue = false; // 方法執行結束前 設定為 false } private void considerNotify(ObserverWrapper observer) { if (!observer.mActive) { // 未啟用狀態直接返回 return; } // 判斷是否可以是啟用狀態 // LifecycleBoundObserver 中則是判斷所在元件的生命週期是否為啟用狀態 if (!observer.shouldBeActive()) { observer.activeStateChanged(false); // 將 observer 的 mActive 設定為 fasle return; } // 如果 observer 的版本號 大於 LiveData 本身的版本號 則直接返回 if (observer.mLastVersion >= mVersion) { return; } // 將 observer 的版本號和 LiveData 本身的版本號同步 observer.mLastVersion = mVersion; // 觸發其 onChanged 方法回撥 observer.mObserver.onChanged((T) mData); }
setValue 的原始碼並不複雜,總結一下:
從原始碼中我們可以瞭解到,當呼叫 LiveData.observer 時,我們傳入的 observer 物件會被包裝成為 LifecycleBoundObserver,會自動感知所在元件的生命週期;
又因為 Lifecycle 會在觀察元件生命週期之後就會進行狀態同步,所以我們再呼叫 LiveData.observer 之後會觸發一次 activeStateChanged,導致 observer 的 mActive 由 fasle 變為 true,所以會進行一次 dispatchingValue;
在範例程式碼中我們給 MainViewModel 中的 number 賦值了初始值 0,那麼初始化時會呼叫 LiveData 有參的建構函式,其中對 mVersion 進行了 +1 操作,此時的 LiveData 中的 mVersion 變為了 0,而 observer 中的 mLastVersion 為 -1,所以會進行一次分發,所以 TextView 的 text 被設定為了 0;
而第二個問題和上述的原因類似,不過特殊點在於 number 是被定義在在 ViewModel 中,開頭也提到過 ViewModel 暫時可以理解為生命週期長於 Activity 的物件,那麼當 Activity 由於橫豎屏切換導致重建後, ViewModel 中的資料並沒有清楚,LiveData 自然保持著他的 mData 最新值以及其 mVersion 版本號,當 Actvitiy 重新呼叫 LiveData.observer 進行訂閱時,傳入的 observer 的 mVersion 已經變為 -1,所以同樣會觸發一次 onChanged 回撥得到最新值;
上述問題答疑中其實可以看出 LiveData 訂閱後可以獲取最新值這在資料流中屬於粘性 事件。在範例程式碼中,橫豎屏切換後仍然可以獲取最新的值,這比較符合使用者使用習慣。但實際開發中往往有著更復雜的場景,比如:定義一個 LiveData<Boolean>(false) 表示是否需要展示載入中彈窗,假設需求是使用者點選按鈕後展示,此時使用者點選按鈕,將其設定為 true,那麼此時 Activiy 發生重建導致生命週期重新走一遍,此時的 LiveData 的 value 仍然為 true,重建後用戶並沒有點選按鈕但彈窗仍然會顯示;
這是一個很常見的業務需求,發生這種問題的根本原因是生命週期重新走之後導致 observer 的 mLastVersion 變更為 -1,而 LiveData 的 mVersion 不變,導致重新觸發 onChanged 方法回撥;
遇到這種情況該怎麼辦呢?難道 LiveData 設計的有問題?我認為這並非 google 官方設計的不好,而是 LiveData 本身就應該作用於時時刻刻需要獲取最新值的場景,而並非所有的資料都需要放到 ViewModel 中用 LiveData 包裹。上述的問題更多的我認為是 LiveData 濫用而導致的。 但 LiveData 的 onChanged 的資料變化後進行回撥很多場景使用起來又很方便,該怎麼辦?
既然已經知道原因,原始碼又瞭解的差不多,很容易就能找到問題的切入點;那就是 considerNotify 方法中會有層層判斷,只要有一個不符合則不會觸發 onChanged 方法回撥,可以反射修改 observer 的 mLastVersion 使其重新訂閱後仍然和 LiveData 保持一致。 不過利用到了反射,那麼風險度也自然提高。
還有更好的辦法,SingleLiveData!我最初看到這個類是在 github 中的一個 issue 中,後來網上流傳了很多版本,其原理是對 LiveData 進行包裝,內部定義一個 HashMap<Observer<in T>, AtomicBoolean> 容器,重寫其 observer 訂閱方法,每個 observer 對應一個 AtomicBoolean 物件,在 setValue 之前先遍歷將所有的 AtomicBoolean 設定為 true,接著重寫其 observer 包裝一層,在分發時判斷並修改 AtomicBoolean 為 false。
我覺得這也是比較好的規避問題的方法,這裡就隨便貼一個了:
class SingleLiveData<T> : MutableLiveData<T>() { private val mPendingMap = HashMap<Observer<in T>, AtomicBoolean>() @MainThread override fun observe(owner: LifecycleOwner, observer: Observer<in T>) { val lifecycle = owner.lifecycle if (lifecycle.currentState == Lifecycle.State.DESTROYED) { return } mPendingMap[observer] = AtomicBoolean(false) lifecycle.addObserver(LifecycleEventObserver { source: LifecycleOwner?, event: Lifecycle.Event -> if (event == Lifecycle.Event.ON_DESTROY) { mPendingMap.remove(observer) } }) super.observe(owner) { t: T -> val pending = mPendingMap[observer] if (pending != null && pending.compareAndSet(true, false)) { observer.onChanged(t) } } } @MainThread override fun observeForever(observer: Observer<in T>) { mPendingMap[observer] = AtomicBoolean(false) super.observeForever(observer) } @MainThread override fun removeObserver(observer: Observer<in T>) { mPendingMap.remove(observer) super.removeObserver(observer) } @MainThread override fun removeObservers(owner: LifecycleOwner) { mPendingMap.clear() super.removeObservers(owner) } @MainThread override fun setValue(t: T?) { for (value in mPendingMap.values) { value.set(true) } super.setValue(t) } }
我對於 LiveData 和網路上認為需要用 Flow 替換 LiveData 的觀點不同,我覺得 LiveData 和 Flow 其實應該共存,或者說是結合實際場景具體選擇,在需要繫結生命週期的場景下 LiveData 就是最佳選擇,沒必要強行使用 Flow,雖然 Flow 也提供了關聯生命週期的做法,但如果專案中已經大面積使用 LiveData 真的沒必要強行去替換,尤其是 Java Kotlin 結合的專案,Java 不支援 Flow 的情況下使用 LiveData 是最佳的選擇;
以上就是Android Jetpack 元件LiveData原始碼解析的詳細內容,更多關於Android Jetpack LiveData的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45