Android Jetpack 之 LiveData

zhich發表於2018-11-29

概述

  • LiveData 是一個持有資料的類,它持有的資料是可以被觀察者訂閱的,當資料被修改時就會通知觀察者。觀察者可以是 Activity、Fragment、Service 等。
  • LiveData 能夠感知觀察者的生命週期,只有當觀察者處於啟用狀態(STARTED、RESUMED)才會接收到資料更新的通知,在未啟用時會自動解註冊觀察者,以減少記憶體洩漏。
  • 使用 LiveData 儲存資料時,由於資料和元件是分離的,當元件重建時可以保證資料不會丟失。

優點

  • 確保 UI 介面始終和資料狀態保持一致。
  • 沒有記憶體洩漏,觀察者繫結到 Lifecycle 物件並在其相關生命週期 destroyed 後自行解除繫結。
  • 不會因為 Activity 停止了而奔潰,如 Activity finish 了,它就不會收到任何 LiveData 事件了。
  • UI 元件只需觀察相關資料,不需要停止或恢復觀察,LiveData 會自動管理這些操作,因為 LiveData 可以感知生命週期狀態的更改。
  • 在生命週期從非啟用狀態變為啟用狀態,始終保持最新資料,如後臺 Activity 在返回到前臺後可以立即收到最新資料。
  • 當配置發生更改(如螢幕旋轉)而重建 Activity / Fragment,它會立即收到最新的可用資料。
  • LiveData 很適合用於元件(Activity / Fragment)之間的通訊。

使用

新增相關依賴

LiveData 有兩種使用方式,結合 ViewModel 使用以及直接繼承 LiveData 類。

結合 ViewModel 使用

以下程式碼場景:點選按鈕提示一個名字。

class MyViewModel : ViewModel() { 
// 建立一個 String 型別的 LiveData // MutableLiveData 是抽象類 LiveData 的子類,我們一般使用的是 MutableLiveData private lateinit var name: MutableLiveData<
String>
fun getName(): MutableLiveData<
String>
{
if (!::name.isInitialized) {
name = MutableLiveData()
} return name
}
}複製程式碼
class LiveDataActivity : AppCompatActivity() { 
private lateinit var myViewModel: MyViewModel override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) setContentView(R.layout.activity_live_data) // 建立並註冊觀察者 myViewModel = ViewModelProviders.of(this).get(MyViewModel::class.java) myViewModel.getName().observe(this, Observer {
// LiveData 資料更新回撥,it 代表被觀察物件的資料,此處為 name Toast.makeText(baseContext, it, Toast.LENGTH_SHORT).show()
}) btnSetName.setOnClickListener {
// 使用 setValue 的方式更新 LiveData 資料 myViewModel.getName().value = "張三"
}
}
}複製程式碼

讓資料(name)和元件(LiveDataActivity)分離,當 Activity 重建時,資料(name)不會丟失。

直接繼承 LiveData 類

以下程式碼場景:在 Activity 中監聽 Wifi 訊號強度。

class WifiLiveData private constructor(context: Context) : LiveData<
Int>
() {
private var mContext: WeakReference<
Context>
= WeakReference(context) companion object {
private var instance: WifiLiveData? = null fun getInstance(context: Context): WifiLiveData {
if (instance == null) {
instance = WifiLiveData(context)
} return instance!!
}
} override fun onActive() {
super.onActive() registerReceiver()
} override fun onInactive() {
super.onInactive() unregisterReceiver()
} /** * 註冊廣播,監聽 Wifi 訊號強度 */ private fun registerReceiver() {
val intentFilter = IntentFilter() intentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION) mContext.get()!!.registerReceiver(mReceiver, intentFilter)
} /** * 登出廣播 */ private fun unregisterReceiver() {
mContext.get()!!.unregisterReceiver(mReceiver)
} private val mReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
when (intent.action) {
WifiManager.RSSI_CHANGED_ACTION ->
getWifiLevel()
}
}
} private fun getWifiLevel() {
val wifiManager = mContext.get()!!.applicationContext.getSystemService(android.content.Context.WIFI_SERVICE) as WifiManager val wifiInfo = wifiManager.connectionInfo val level = wifiInfo.rssi instance!!.value = level // 傳送 Wifi 的訊號強度給觀察者
}
}複製程式碼
class LiveDataActivity : AppCompatActivity() { 
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) setContentView(R.layout.activity_live_data) withExtendsLiveDataTest()
} /** * 直接繼承 LiveData 類 */ private fun withExtendsLiveDataTest() {
WifiLiveData.getInstance(this).observe(this, Observer {
Log.e("LiveDataActivity", it.toString()) // 觀察者收到資料更新的通知,列印 Wifi 訊號強度
})
}
}複製程式碼

當元件(Activity)處於啟用狀態(onActive)時註冊廣播,處於非啟用狀態(onInactive)時登出廣播。

原始碼解析

observe 註冊流程

LiveData 通過 observe() 方法將被觀察者 LifecycleOwner (Activity / Fragment) 和觀察者 Observer 關聯起來。

LiveData.observe(LifecycleOwner owner , Observer<
T>
observer)複製程式碼

進入 LiveData 的 observe() 方法中

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<
T>
observer)
{
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// 若 LifecycleOwner 處於 DESTROYED 狀態,則返回 return;

} // LifecycleBoundObserver 把 LifecycleOwner 物件和 Observer 物件包裝在一起 LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
// mObservers(類似 Map 的容器)的 putIfAbsent() 方法用於判斷容器中的 observer(key) // 是否已有 wrapper(value)與之關聯 // 若已關聯則直接返回關聯值,否則關聯後再返回 wrapper 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;

} // 由於 LifecycleBoundObserver 實現了 GenericLifecycleObserver 介面,而 GenericLifecycleObserver 又 // 繼承了 LifecycleObserver,所以 LifecycleBoundObserver 本質是一個 LifecycleObserver // 此處屬於註冊過程, Lifecycle 新增觀察者 LifecycleObserver owner.getLifecycle().addObserver(wrapper);

}複製程式碼

從上面的程式碼可知,observe() 方法最終是會呼叫 LifecycleOwner.getLifecycle().addObserver(LifecycleObserver) ,因此 LiveData 是能夠感知觀察者的生命週期變化的。

感知生命週期變化

通過以上的分析,我們知道 LifecycleBoundObserver(LiveData 的內部類)是觀察者,以下具體分析 LifecycleBoundObserver 的實現過程。

class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver { 
@NonNull final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<
T>
observer) {
super(observer);
// 儲存 Observer mOwner = owner;
// 儲存 LifecycleOwner
} @Override boolean shouldBeActive() {
// 判斷是否處於啟用狀態 return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);

} @Override public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
// 若 Lifecycle 處於 DESTROYED 狀態,則移除 Observer 物件 if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
// 移除觀察者,在這個方法中會移除生命週期監聽並且回撥 activeStateChanged() 方法 removeObserver(mObserver);
return;

} // 若處於啟用狀態,則呼叫 activeStateChanged() 方法 activeStateChanged(shouldBeActive());

} @Override boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;

} @Override void detachObserver() {
mOwner.getLifecycle().removeObserver(this);

}
}複製程式碼

當元件(Activity / Fragment)的生命週期發生改變時,onStateChanged() 方法將會被呼叫。若當前處於 DESTROYED 狀態,則會移除觀察者;若當前處於啟用狀態,則會呼叫 activeStateChanged() 方法。activeStateChanged() 方法位於父類 ObserverWrapper 中。

void activeStateChanged(boolean newActive) { 
// 若新舊狀態一致,則返回 if (newActive == mActive) {
return;

} // immediately set active state, so we'd never dispatch anything to inactive owner mActive = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
LiveData.this.mActiveCount += mActive ? 1 : -1;
if (wasInactive &
&
mActive) {
// 啟用狀態的 observer 個數從 0 到 1 onActive();
// 空實現,一般讓子類去重寫
} if (LiveData.this.mActiveCount == 0 &
&
!mActive) {
// 啟用狀態的 observer 個數從 1 到 0 onInactive();
// 空實現,一般讓子類去重寫
} if (mActive) {
// 啟用狀態,向觀察者傳送 LiveData 的值 dispatchingValue(this);

}
}複製程式碼

再看看最終呼叫的 dispatchingValue() 方法。

private void dispatchingValue(@Nullable ObserverWrapper initiator) { 
// ... do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;

} else {
// 迴圈遍歷 mObservers 這個 map , 向每一個觀察者都傳送新的資料 for (Iterator<
Map.Entry<
Observer<
T>
, ObserverWrapper>
>
iterator = mObservers.iteratorWithAdditions();
iterator.hasNext();
) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;

}
}
}
} while (mDispatchInvalidated);
// ...
}複製程式碼

可以看到 dispatchingValue() 方法裡面再通過 considerNotify() 方法將訊息通知下去。

private void considerNotify(ObserverWrapper observer) { 
// ... observer.mObserver.onChanged((T) mData);

}複製程式碼

上面的 mObserver 正是我們呼叫 observe() 方法時傳入的觀察者。

總結上面的分析就是:呼叫 LiveData.observe(LifecycleOwner owner , Observer observer) 進行註冊後,當 LiveData 資料發生變化後,最終就會呼叫 Observer 物件的 onChanged() 方法,並把變化的資料作為引數回傳。

通知觀察者更新資料的方式

LiveData 為我們提供了兩種改變資料後,通知觀察者更新資料的方式,一個是 setValue() 方法(必須在主執行緒呼叫),另一個是 postValue() 方法(必須在子執行緒呼叫)。

setValue() 方法

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

}複製程式碼

dispatchingValue() 方法會跑我們上面分析的流程,最終把改變的資料 value(對應上面的 mData)作為 onChanged() 方法的引數傳給觀察者。

postValue() 方法

protected void postValue(T value) { 
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;

} if (!postTask) {
return;

} ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);

}private final Runnable mPostValueRunnable = new Runnable() {
@Override public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;

} //noinspection unchecked setValue((T) newValue);

}
};
複製程式碼

可以看出 postValue() 方法最終也會在主執行緒中呼叫 setValue() 方法。

文中 Demo GitHub 地址

參考資料:

來源:https://juejin.im/post/5bff376bf265da613e21f42d#comment

相關文章