1 概述
LiveData是一個可被觀察的資料持有類,一般的資料類不同,LiveData是生命週期感知的,資料類的生命週期可以和其他app元件的生命週期保持一致,例如Activity,fragment和service。這保證了LiveData僅僅會更新處在活動狀態的元件。
LiveData可以被看成觀察者模式的實踐,LiveData是一個被觀察的物件,其他元件會訂閱對它的觀察,當元件處於Started
或者Resumed
則為活躍狀態,LiveData只會通知處於這兩者狀態的元件更新。同時希望在元件變為Destroyed
狀態時去自動銷燬LiveData和元件的繫結,不至於出現洩漏。
1.1 優勢
-
確保UI和資料狀態同步
-
沒有記憶體洩漏
-
不會因為已經停止的Activity導致crash
-
不需要手動處理生命週期
-
元件時刻保持最新資料
-
支援適當配置更改
如果一個Activity或者fragment因為例如裝置旋轉而重新建立,它會立即接受到最新可獲得的資料。
-
共享資源 多個元件可以共享同一份資料
2 實踐
2.1 依賴
LiveData是Android官方架構Jetpack的組成部分,架構元件全部在google的Maven倉庫裡,想要使用可以在專案的build.gradle檔案裡新增google()
依賴。
allprojects {
repositories {
google() //引入livedata
jcenter()
}
}
複製程式碼
在模組的build.gradle檔案中加入lifecycle:extensions依賴
implementation "android.arch.lifecycle:extensions:1.1.1"
複製程式碼
2.2 建立LiveData物件
LiveData通常和Jetpack架構下的另一個元件ViewModel
配合使用,ViewModel是一個負責為Activity或者Fragment準備和管理資料的類,同時處理和應用剩餘部分的通訊,注意ViewModel僅僅負責管理UI上的資料,其他都無權干涉,它和元件生命週期繫結,只有Activity結束了,它才會被銷燬。
我們建立一個提供電池電量資訊的ViewModel:
public class BatteryViewModel extends ViewModel {
private MutableLiveData<Integer> currentBattery;
public MutableLiveData<Integer> getCurrentBatteryData() {
if (currentBattery == null) {
currentBattery = new MutableLiveData<>();
}
return currentBattery;
}
}
複製程式碼
這裡使用了MutableLiveData
類來儲存電量資料,它繼承了LiveData,暴露了setValue
、postValue
方法。
public class MutableLiveData<T> extends LiveData<T> {
@Override
public void postValue(T value) {
super.postValue(value);
}
@Override
public void setValue(T value) {
super.setValue(value);
}
}
複製程式碼
2.3 監聽LiveData物件
寫一個activity模擬顯示電量變化情況,通常我們要在onCreate回撥裡開始監聽LiveData,主要出於以下原因:
- onCreate是activity建立時的第一個回撥,onResume和onStart在activity生命週期內會回撥多次,造成呼叫監聽多次形成冗餘。
- 確保activity和fragment在變成活躍狀態進入started時可以儘快獲得資料更新,所以要儘早開始監聽。
LiveData在用非活躍狀態進入活躍狀態時同樣可以接受到更新,但是如果第二次從非活躍狀態進入活躍狀態,那麼只有當上一次變成活躍態的資料發生變化時才會接受更新。
public class BatteryActivity extends AppCompatActivity {
private int battery = 100;
private BatteryViewModel mBatteryViewModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_livedata_battery);
//在當前activity範圍內建立或者獲得viewmodel例項
mBatteryViewModel = ViewModelProviders.of(this).get(BatteryViewModel.class);
final TextView tvBattery = findViewById(R.id.tv_battery);
//設定觀察者
Observer<Integer> batteryOb = new Observer<Integer>() {
@Override
public void onChanged(@Nullable Integer integer) {
tvBattery.setText(String.format("電量:^[0-9]*$", integer));
}
};
//將資料繫結到觀察者
mBatteryViewModel.getCurrentBatteryData().observe(this, batteryOb);
}
}
複製程式碼
ViewModelProviders
是lifecycle:extensions模組下的類,首先通過of(this)
建立一個ViewModelProvider例項,再通過get方法去獲得(如果有)或者建立一個viewmodel,然後建立一個Observer
,將它和viewmodel繫結,監聽資料的變化,在onChanged
回撥內實時修改UI。
2.4 更新LiveData資料
監聽設定好之後,下面可以通過修改資料看看ui是否被實時修改,點選開始統計按鈕,讓電量減一來模擬掉電情況。
LiveData更改資料的方法不是public型別的,只在內部自己呼叫,所有這裡才會使用MutableLiveData,他暴露了修改資料的公共方法。這裡修改電量減一就是用到了setValue方法。
findViewById(R.id.btn_start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mBatteryViewModel.getCurrentBatteryData().postValue(battery--);
}
});
複製程式碼
可以看到MutableLiveData
總共有兩個修改資料的方法,他們的區別是什麼呢?
- setValue是必須在主執行緒被呼叫,用來修改LiveData資料。
- postValue可以在後臺執行緒呼叫,它是向執行緒的觀察者傳送一個task,請求修改資料,但是如果在主執行緒執行前呼叫多次,則只有最後一次會生效。
3 擴充套件LiveData
上面是使用自帶的MutableLiveData,同樣也可以自己定義LiveData類。
3.1 自定義LiveData
自定義一個BatteryLiveData類繼承自LiveData,通過廣播去接受系統電池電量,通過setValue將資料設定給LiveData。
public class BatteryLiveData extends LiveData<Integer> {
private Context context;
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
setValue(intent.getIntExtra("level", 0));
}
};
public BatteryLiveData(Context context) {
this.context = context;
}
@Override
protected void onActive() {
super.onActive();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
context.registerReceiver(receiver, filter);
}
@Override
protected void onInactive() {
super.onInactive();
context.unregisterReceiver(receiver);
}
}
複製程式碼
需要重寫兩個方法:
- onActive() 當LiveData繫結有活躍狀態的observer時就會呼叫,在這裡回去註冊廣播獲得電池電量變化。
- onInactive() 當LiveData沒有任何活躍狀態observer繫結時呼叫,取消註冊廣播。
3.2 獲得資料更新
在activity裡構造LiveData例項,同時呼叫observe()
方法將activity和LiveData繫結。
BatteryLiveData batteryLiveData = new BatteryLiveData(this);
batteryLiveData.observe(this, new Observer<Integer>() {
@Override
public void onChanged(@Nullable Integer integer) {
tvBattery.setText(integer.toString());
}
});
複製程式碼
3.3 共享資源
文章一開始講到LiveData的優勢之一就是共享資源,可以將LiveData設計成單例模式,在任何需要的地方呼叫observe()繫結監聽。
public class BatteryLiveData extends LiveData<Integer> {
...
@MainThread
public static BatteryLiveData getInstance(Context context) {
if (sInstance == null) {
sInstance = new BatteryLiveData(context);
}
return sInstance;
}
private BatteryLiveData(Context context) {
this.context = context;
}
...
}
BatteryLiveData.getInstance(this).observe(this, new Observer<Integer>() {
@Override
public void onChanged(@Nullable Integer integer) {
tvBattery.setText(integer.toString());
}
});
複製程式碼
4 LiveData轉換
4.1 map
map是將一個LiveData轉換成另一個LiveData。
Transformations.map(batteryLiveData, new Function<Integer, String>() {
@Override
public String apply(Integer input) {
return input + "%";
}
});
複製程式碼
4.2 switchMap
第二個引數接收一個方法,通過方法將傳入的第一個引數LiveData轉換成另一個LiveData。
Transformations.switchMap(sInstance, new Function<Integer, LiveData<? extends String>>() {
@Override
public LiveData<? extends String> apply(Integer input) {
return ...;
}
});
複製程式碼
5 原始碼解析
下面來分析一下LiveData的關鍵原始碼。先從LiveData類開始:
LiveData裡面主要有幾個上面用到的方法,observe
,setValue
,postValue
,onActive
,onInactive
等,挨個來分析。
5.1 observe()
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
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;
}
owner.getLifecycle().addObserver(wrapper);
}
public interface LifecycleOwner {
@NonNull
Lifecycle getLifecycle();
}
複製程式碼
observe有兩個引數,第一個是LifecycleOwner
,Fragment和FragmentActivity等都實現了該介面,因此我們在呼叫的時候就可以直接傳入this,它裡面有一個方法可以獲得當前的Lifecycle例項,Lifecycle
裡面儲存了和生命週期相對應的狀態。
observe方法首先先判斷當前狀態是不是DESTROYED
,如果是就可以完全忽略,因為已經說過只對處於活躍狀態的元件做更新;接著將owner observer構造成LifecycleBoundObserver
例項,這是一個內部類,裡面有關於狀態變換的一系列操作,待會詳細分析;然後將observer和wrapper存入map快取中,如果observer快取已存在並且已經和另一個LifecycleOwner
繫結,則丟擲異常;如果快取已經存在則直接忽略;最後呼叫addObserver方法將LifecycleBoundObserver
例項和LifecycleOwner
繫結。而addObserver是呼叫了LifecycleRegistry
類的實現。
5.2 ObserverWrapper
private abstract class ObserverWrapper {
final Observer<T> mObserver;
boolean mActive;
int mLastVersion = START_VERSION;
ObserverWrapper(Observer<T> observer) {
mObserver = observer;
}
abstract boolean shouldBeActive();
boolean isAttachedTo(LifecycleOwner owner) {
return false;
}
void detachObserver() {
}
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;
//過去是inactive,現在是active
if (wasInactive && mActive) {
onActive();
}
//過去沒有訂閱,並且現在是inactive
if (LiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
//現在是active
if (mActive) {
dispatchingValue(this);
}
}
}
class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
...
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}
}
複製程式碼
ObserverWrapper裡面封裝了關於狀態的操作,包括判斷是否處於活躍狀態、observer是否繫結到lifecycleowner以及更改activity狀態等。
activeStateChanged首先判斷新來的狀態和舊狀態是否相同,相同則忽略,然後判斷LiveData上的活躍態的數量是否為0,為0說明之前處於Inactive,然後統計現在的訂閱數,接著就是三個if判斷,註釋在程式碼裡。正式這三個判斷,LiveData可以接收到onActive和onInactive的回撥。
dispatchingValue(this)是當狀態變為active時呼叫,用來更新資料。裡面會用到considerNotify方法。
5.3 setValue postValue
private final Runnable mPostValueRunnable = new Runnable() {
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
//noinspection unchecked
setValue((T) newValue);
}
};
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(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;
//noinspection unchecked
observer.mObserver.onChanged((T) mData);
}
複製程式碼
setValue會呼叫dispatchingValue方法,接著呼叫considerNotify,在最後呼叫onChange()回撥,就能收到資料變化。
postValue之前說過是往主執行緒傳送事件,同時加鎖保持佔用,防止多執行緒併發競爭導致的資料錯誤,因為每次postValue成功都會對mPendingData重置為NOT_SET。然後想主執行緒傳送Runnable物件,Runnable例項的run方法會執行setValue在主執行緒修改資料。