零. 前言
上篇文章《萬物基于Lifecycle》 介紹了整個Lifecycle體系的基石,今天這篇文章咱們來看看Jetpack給我們帶來的活著的數(shù)據(jù)——LiveData饿这。
大綱
- LiveData 是什么浊伙?
- 為什么要用LiveData?
- How to use LiveData?
- LiveData的生命感知能力從何而來撞秋,是如何與Lifecycle結(jié)合的?
一. LiveData 是什么吧黄?
? LiveData 簡單來說部服,就是普通數(shù)據(jù)對象的一個包裝類,這個包裝類中幫助你主動管理了數(shù)據(jù)的版本號拗慨,基于觀察者模式廓八,讓普通的數(shù)據(jù)對象能夠感知所屬宿主(Activity、Fragment)的生命周期赵抢。這種感知能力就能夠保證只有宿主活躍(Resumed剧蹂、Started)時,數(shù)據(jù)的觀察者才能受到數(shù)據(jù)變化的消息烦却。
上面這短短的一段話宠叼,卻有著重大的意義,舉一個case:
有一個頁面需要加載一個列表其爵,我們需要在后臺線程去服務(wù)器請求對應(yīng)的數(shù)據(jù)冒冬,請求數(shù)據(jù)成功并經(jīng)過解析后post消息通知UI,UI再渲染請求來的數(shù)據(jù)摩渺。等等简烤!假若此時的網(wǎng)絡(luò)很慢,而剛好用戶此時按home鍵退出了應(yīng)用摇幻,那么在此時UI是不應(yīng)該被渲染的横侦,而是應(yīng)該等用戶重新進入應(yīng)用時才開始渲染。
從下面的演示代碼就詮釋了上面的說所的case
class MainActivity extends AppcompactActivity{
public void onCreate(Bundle bundle){
Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
//無論頁面可見不可見绰姻,都會去執(zhí)行頁面刷新,IO枉侧。更有甚者彈出對話框
}
};
//1.無論當(dāng)前頁面是否可見,這條消息都會被分發(fā)。----消耗資源
//2.無論當(dāng)前宿主是否還存活,這條消息都會被分發(fā)狂芋。---內(nèi)存泄漏
handler.sendMessage(msg)
liveData.observer(this,new Observer<User>){
void onChanged(User user){
}
}
//1.減少資源占用--- 頁面不可見時不會派發(fā)消息
//2.確保頁面始終保持最新狀態(tài)---頁面可見時,會立刻派發(fā)最新的一條消息給所有觀察者--保證頁面最新狀態(tài)
//3.不再需要手動處理生命周期---避免NPE
//4.可以打造一款不用反注冊,不會內(nèi)存泄漏的消息總線---取代eventbus
liveData.postValue(data);
}
}
有人說榨馁,我可以在處理消息時,根據(jù)當(dāng)前頁面時是否可見來具體處理對應(yīng)邏輯帜矾。是的翼虫,沒錯,確實可以這樣,就像下面這樣黍特。
Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
if (isActivityValid()) {
// updateUI...
} else {
// dosomething...
}
}
};
我再拿上面的例子說一下這種問題:
- 需要自行判斷宿主活躍狀態(tài)蛙讥,防止生命周期越界。
- 如果Activity不可見灭衷,此時不更新UI次慢,那么就需要復(fù)寫onResume方法去更新UI,手工管理生命周期,增加了代碼的復(fù)雜性迫像。
二. 為什么要使用LiveData?
上面的例子已經(jīng)很好地詮釋了LiveData的強大劈愚,當(dāng)然不僅限于此,它的優(yōu)勢如下:
-
確保界面符合數(shù)據(jù)狀態(tài)
LiveData 遵循觀察者模式闻妓。當(dāng)生命周期狀態(tài)發(fā)生變化時菌羽,LiveData 會通知
Observer
對象。觀察者可以在onChanged事件時更新界面由缆,而不是在每次數(shù)據(jù)發(fā)生更改時立即更新界面注祖。 -
不會發(fā)生內(nèi)存泄漏
觀察者會綁定到
Lifecycle
對象,并在其關(guān)聯(lián)的生命周期遭到銷毀后進行自我清理均唉。 -
不會因 Activity 停止而導(dǎo)致崩潰
如果觀察者的生命周期處于非活躍狀態(tài)(如返回棧中的 Activity)是晨,則它不會接收任何 LiveData 事件。
-
不再需要手動處理生命周期
界面組件只是觀察相關(guān)數(shù)據(jù)舔箭,不會停止或恢復(fù)觀察罩缴。LiveData 將自動管理所有這些操作,因為它在觀察時可以感知相關(guān)的生命周期狀態(tài)變化层扶。
-
數(shù)據(jù)始終保持最新狀態(tài)
如果生命周期變?yōu)榉腔钴S狀態(tài)箫章,它會在再次變?yōu)榛钴S狀態(tài)時接收最新的數(shù)據(jù)。例如镜会,曾經(jīng)在后臺的 Activity 會在返回前臺后立即接收最新的數(shù)據(jù)檬寂。
-
適當(dāng)?shù)呐渲酶?/strong>
如果由于配置更改(如設(shè)備旋轉(zhuǎn))而重新創(chuàng)建了 Activity 或 Fragment,它會立即接收最新的可用數(shù)據(jù)稚叹。
-
共享資源
可以使用單一實例模式擴展
LiveData
對象以封裝系統(tǒng)服務(wù)焰薄,以便在應(yīng)用中共享它們拿诸。LiveData
對象連接到系統(tǒng)服務(wù)一次扒袖,然后需要相應(yīng)資源的任何觀察者只需觀察LiveData
對象 支持黏性事件的分發(fā)
即先發(fā)送一條數(shù)據(jù),后注冊一個觀察者亩码,默認是能夠收到之前發(fā)送的那條數(shù)據(jù)的
三. How to use LiveData ?
step1: 添加依賴:
step2: 在ViewModel中創(chuàng)建 LiveData
實例 (ViewModel組件會在下期講到季率,將LiveData存儲至viewModel中,是為了符合MVVM架構(gòu)思想描沟,V層僅負責(zé)展示飒泻,而VM層負責(zé)數(shù)據(jù)邏輯)
public class ViewModelTest extends AndroidViewModel {
public final MutableLiveData<String> name = new MutableLiveData<>();
...
}
step3: 在Activity或Fragment中對LiveData進行添加觀察者進行監(jiān)聽
public class ActivityTest extends AppCompatActivity {
...
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
viewModel.name.observe(this, new Observer<String>() {
@Override
public void onChanged(@Nullable String name) {
// dosomething...
}
});
}
}
我們可以根據(jù)LiveData值的變化來做對應(yīng)的事情,且不用擔(dān)心生命周期越界的問題吏廉。
LiveData核心方法
方法名 | 作用 |
---|---|
observe(LifecycleOwner owner,Observer observer) | 注冊和宿主生命周期關(guān)聯(lián)的觀察者 |
observeForever(Observer observer) | 注冊觀察者,不會反注冊,需自行維護 |
setValue(T data) | 發(fā)送數(shù)據(jù),沒有活躍的觀察者時不分發(fā)泞遗。只能在主線程。 |
postValue(T data) | 和setValue一樣席覆。不受線程環(huán)境限制, |
onActive | 當(dāng)且僅當(dāng)有一個活躍的觀察者時會觸發(fā) |
inActive | 不存在活躍的觀察者時會觸發(fā) |
LiveData的衍生類及功能
-
MutableLiveData
該類十分簡單史辙,主要開放了LiveData的發(fā)消息接口
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); } }
設(shè)計初衷:考慮單一開閉原則,LiveData只能接受消息,避免拿到LiveData對象既能發(fā)消息也能收消息的混亂使用聊倔。
-
MediatorLiveData
合并多個LiveData, 即一對多統(tǒng)一觀察晦毙,一個經(jīng)典的場景是:在向服務(wù)器請求數(shù)據(jù)時,優(yōu)先展示本地數(shù)據(jù)庫的數(shù)據(jù)耙蔑,然后由請求的響應(yīng)決定是否要更新數(shù)據(jù)庫见妒,如下圖所示:
// ResultType: Type for the Resource data. // RequestType: Type for the API response. public abstract class NetworkBoundResource<ResultType, RequestType> { // MediatorLiveData 數(shù)據(jù)組合者 private final MediatorLiveData<Resource<ResultType>> result = new MediatorLiveData<>(); private Executor executor; @MainThread protected NetworkBoundResource(Executor mExecutor) { this.executor = mExecutor; // 首先初始化一個Loading的status 空result result.setValue(Resource.loading(null)); // 然后從數(shù)據(jù)庫中獲取持久化數(shù)據(jù) LiveData<ResultType> dbSource = loadFromDb(); // 數(shù)據(jù)組合者監(jiān)聽數(shù)據(jù)庫中的數(shù)據(jù) result.addSource(dbSource, data -> { // dbSource第一次回調(diào),用來判斷數(shù)據(jù)有效期甸陌,此時取消監(jiān)聽 result.removeSource(dbSource); // 業(yè)務(wù)自行定義是否需要fetch最新的數(shù)據(jù) if (shouldFetch(data)) { fetchFromNetwork(dbSource); } else { // 數(shù)據(jù)有效须揣,重新觀察一次,觀察者會立馬收到一次回調(diào)(LiveData粘性事件機制) result.addSource(dbSource, newData -> result.setValue(Resource.success(newData))); } }); } private void fetchFromNetwork(final LiveData<ResultType> dbSource) { LiveData<ApiResponse<RequestType>> apiResponse = createCall(); // 這里數(shù)據(jù)雖無效钱豁,但是可以先給UI展示 result.addSource(dbSource, newData -> setValue(Resource.loading(newData))); result.addSource(apiResponse, response -> { result.removeSource(apiResponse); result.removeSource(dbSource); if (response != null) { if (response.isSuccessful()) { executor.execute(() -> { saveCallResult(processResponse(response)); executor.execute(() -> // 這里我們拿到的最新的數(shù)據(jù)需要主動通知監(jiān)聽者量蕊,以拿到從服務(wù)端拿到的最新數(shù)據(jù) result.addSource(loadFromDb(), newData -> setValue(Resource.success(newData))) ); }); } else { onFetchFailed(); result.addSource(dbSource, newData -> setValue(Resource.error(response.errorMessage, newData))); } } else { result.addSource(dbSource, newData -> setValue(Resource.error("Request failed, server didn't response", newData))); } }); } }
上面的例子MediatorLiveData同時監(jiān)聽了數(shù)據(jù)庫中的LiveData和服務(wù)端的LiveData
-
Transformations
這是一個數(shù)據(jù)轉(zhuǎn)化工具類灵寺,共兩個主要方法:
-
靜態(tài)轉(zhuǎn)化 -- map(@NonNull LiveData<X> source, @NonNull final Function<X, Y> mapFunction)
MutableLiveData<Integer> data = new MutableLiveData<>(); //數(shù)據(jù)轉(zhuǎn)換 LiveData<String> transformData = Transformations.map(data, input -> String.valueOf(input)); //使用轉(zhuǎn)換后生成的transformData去觀察數(shù)據(jù) transformData.observe( this, output -> { }); //使用原始的livedata發(fā)送數(shù)據(jù) data.setValue(10);
-
- 動態(tài)轉(zhuǎn)化 -- LiveData<Y> switchMap(
@NonNull LiveData<X> source,
@NonNull final Function<X, LiveData<Y>> switchMapFunction)
MutableLiveData userIdLiveData = ...;
// 用戶數(shù)據(jù)和用戶id緊密相關(guān),當(dāng)我們改變userId的liveData的同時還會主動通知userLiveData更新
LiveData userLiveData = Transformations.switchMap(userIdLiveData, id ->
repository.getUserById(id));
void setUserId(String userId) {
this.userIdLiveData.setValue(userId);
}
// 不要像下面這么做
private LiveData getUserLiveData(String userId) {
// DON'T DO THIS
return repository.getUserById(userId);
}
四. LiveData的實現(xiàn)機制
LiveData注冊觀察者觸發(fā)消息分發(fā)流程:
- observe 注冊時,可以主動跟宿主生命周期綁定先蒋,不用反注冊:
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
//1. 首先來個斷言,這個方法只能在主線程調(diào)用愧捕,observeForever也是量九。
assertMainThread("observe");
//2.其次把注冊進來的observer包裝成 一個具有生命周邊邊界的觀察者
//它能監(jiān)聽宿主被銷毀的事件,從而主動的把自己反注冊塑煎,避免內(nèi)存泄漏
//此時觀察者是否處于活躍狀態(tài)就等于宿主是否可見
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
//3.接著會判斷該觀察是否已經(jīng)注冊過了,如果是則拋異常,所以要注意垮兑,不允許重復(fù)注冊
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
//4.這一步才是關(guān)鍵
//利用Lifecycle冷尉,把觀察者注冊到進去,才能監(jiān)聽到宿主生命周期狀態(tài)的變化系枪,對不對雀哨?
//根據(jù)Lifecycle文章中的分析,一旦一個新的觀察者被添加私爷,Lifecycle也會同步它的狀態(tài)和宿主一致對不對雾棺?此時會觸發(fā)觀察者的onStateChanged方法
owner.getLifecycle().addObserver(wrapper);
}
- LifecycleBoundObserver 監(jiān)聽宿主的生命周期(這里我們還記得之前在Lifecycle解析中提到addObserver時也會對observer進行包裝捌浩,這時一樣的),并且宿主不可見時不分發(fā)任何數(shù)據(jù):
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
}
@Override
boolean shouldBeActive() {
//使用observer方法注冊的觀察者都會被包裝成LifecycleBoundObserver
//觀察者是否活躍就等于宿主 的狀態(tài)是否大于等于STARTED工秩,
//如果頁面當(dāng)前不可見尸饺,你發(fā)送了一條消息麻昼,此時是不會被分發(fā)的,可以避免后臺任務(wù)搶占資源,當(dāng)頁面恢復(fù)可見才會分發(fā)馋辈。
//注意:如果使用observerForever注冊的觀察者抚芦,
//會被包裝成AlwaysActiveObserver,它的shouldBeActive一致返回true.即便在頁面不可見也能收到數(shù)據(jù)
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
//在這里如果監(jiān)聽到宿主被銷毀了,則主動地把自己從livedata的觀察者中移除掉
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
//否則說明宿主的狀態(tài)發(fā)生了變化迈螟,此時會判斷宿主是否處于活躍狀態(tài)
activeStateChanged(shouldBeActive());
}
}
- ObserverWrapper 狀態(tài)變更后叉抡,如果觀察者處于活躍狀態(tài)會觸發(fā)數(shù)據(jù)的分發(fā)流程:
abstract class ObserverWrapper{
final Observer<? super T> mObserver;
boolean mActive;
int mLastVersion = START_VERSION//這里就等于-1,沒有主動和LiveData的mVersion對齊,為黏性事件埋下了伏筆
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
//更改觀察者的狀態(tài)
mActive = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
//如果此時有且只有一個活躍的觀察者則觸發(fā)onActive
LiveData.this.mActiveCount += mActive ? 1 : -1;
if (wasInactive && mActive) {
onActive();
}
//沒有任何一個活躍的觀察者則觸發(fā)onInactive
//利用這個方法被觸發(fā)的時機答毫,可以做很多事褥民,比如懶加載,資源釋放等
if (LiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
//如果此時觀察者處于活躍狀態(tài),下面就開始分發(fā)數(shù)據(jù)了
//請注意洗搂,這里傳遞了this = observer
if (mActive) {
dispatchingValue(this);
}
}
}
- dispatchingValue 數(shù)據(jù)分發(fā)流程控制:
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
//如果傳遞的觀察者不為空消返,則把數(shù)據(jù)分發(fā)給他自己。這個流程是新注冊觀察者的時候會被觸發(fā)
considerNotify(initiator);
initiator = null;
} else {
//否則遍歷集合中所有已注冊的的觀察者耘拇,逐個調(diào)用considerNotify撵颊,分發(fā)數(shù)據(jù)
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
- considerNotify 數(shù)據(jù)真正分發(fā)的地方,需要滿足三個套件:
private void considerNotify(ObserverWrapper observer) {
//觀察者當(dāng)前狀態(tài)不活躍不分發(fā)
if (!observer.mActive) {
return;
}
//觀察者所在宿主是否處于活躍狀態(tài),否則不分發(fā)惫叛,并且更改觀察者的狀態(tài)為false
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
//此處判斷觀察者接收消息的次數(shù)是否大于等于 發(fā)送消息的次數(shù)
//但是observer被創(chuàng)建之初verison=-1
//如果此時LiveData已經(jīng)發(fā)送過數(shù)據(jù)了倡勇。這里就不滿足了,就出現(xiàn)黏性事件了嘉涌,后注冊的觀察者收到了前面發(fā)送的消息妻熊。
if (observer.mLastVersion >= mVersion) {
return;
}
//每分發(fā)一次消息,則把觀察者和LiveData的version對齊仑最,防止重復(fù)發(fā)送
observer.mLastVersion = mVersion;
//最后的數(shù)據(jù)傳遞
observer.mObserver.onChanged((T) mData);
}
普通消息分發(fā)流程扔役。即調(diào)用 postValue,setValue 才會觸發(fā)消息的分發(fā):
五. 總結(jié)
Android開發(fā)大部分主要的工作就是從服務(wù)器獲取數(shù)據(jù)词身,將其轉(zhuǎn)為渲染為UI展示給用戶厅目。本質(zhì)上我們所做的邏輯都是“數(shù)據(jù)驅(qū)動”番枚,所有的View都是數(shù)據(jù)的一種狀態(tài)法严,數(shù)據(jù)映射的過程中我們需要去考慮生命周期的限制--即數(shù)據(jù)的活躍性。LiveData結(jié)合Lifecycle幫我們規(guī)范了這個流程葫笼,完美詮釋了observer深啤、lifecycle-aware、data holder 這個鐵三角路星,開發(fā)者在遵循這個開發(fā)流程的過程中溯街,便完成了UI -> ViewModel -> Data的單項依賴诱桂。