[TOC]
# Lifecycle
## 解決問題:
* activity的生命周期內有大量管理組件的代碼,難以維護。
* 無法保證組件會在 Activity/Fragment停止后不執行啟動
## 實現
LifecycleOwner(如Activity)在生命周期狀態改變時(也就是生命周期方法執行時),遍歷觀察者,獲取每個觀察者的方法上的注解。
# LifeData
**LiveData使得 數據的更新 能以觀察者模式 被observer感知,且此感知只發生在 LifecycleOwner的活躍生命周期狀態**。
## 基本用法
1. 創建LiveData實例,指定源數據類型
2. 創建Observer實例,實現onChanged()方法,用于接收源數據變化并刷新UI
3. LiveData實例使用observe()方法添加觀察者,并傳入LifecycleOwner
4. LiveData實例使用setValue()/postValue()更新源數據 (子線程要postValue())
## LiveData與MutableLiveData區別
1. MutableLiveData的父類是LiveData
2. LiveData在實體類里可以通知指定某個字段的數據更新.
3. MutableLiveData則是完全是整個實體類或者數據類型變化后才通知.不會細節到某個字段
4. LiveData不可變,MutableLiveData是可變的
## 源碼分析(觀察者模式)
LiveData原理是觀察者模式,下面就先從LiveData.observe()方法看起:
~~~
//key 觀察者 value 包裝的LifecycleOwner
private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
new SafeIterableMap<>();
~~~
## 事件推送,解決防止事件丟失
投遞事件時,如果判斷Lifecycle是否激活,如果NO,則把數據暫存起來
## 解決數據倒灌
對于這個問題,總結一下發生的核心原因。對于LiveData,其初始的version是-1,當我們調用了其setValue或者postValue,其vesion會+1;對于每一個觀察者的封裝ObserverWrapper,其初始version也為-1,也就是說,每一個新注冊的觀察者,其version為-1;當LiveData設置這個ObserverWrapper的時候,如果LiveData的version大于ObserverWrapper的version,LiveData就會強制把當前value推送給Observer。
那么能不能從**Map容器mObservers中取到LifecycleBoundObserver,然后再更改version呢**?答案是肯定的,通過查看SafeIterableMap的源碼我們發現有一個protected的get方法。因此,在調用observe的時候,我們可以通過反射拿到LifecycleBoundObserver,再把LifecycleBoundObserver的version設置成和LiveData一致即可。
# ViewModel
## 特點
### 生命周期長于Activity
ViewModel最重要的特點是 生命周期長于Activity。來看下官網的一張圖:
看到在因屏幕旋轉而重新創建Activity后,ViewModel對象依然會保留。 只有Activity真正Finish的時ViewModel才會被清除。
也就是說,因系統配置變更Activity銷毀重建,ViewModel對象會保留并關聯到新的Activity。而Activity的正常銷毀(系統不會重建Activity)時,ViewModel對象是會清除的。
那么很自然的,因系統配置變更Activity銷毀重建,ViewModel內部存儲的數據 就可供重新創建的Activity實例使用了。這就解決了第一個問題。
### 不持有UI層引用
我們知道,在MVP的Presenter中需要持有IView接口來回調結果給界面。
而ViewModel是不需要持有UI層引用的,那結果怎么給到UI層呢?答案就是使用上一篇中介紹的基于觀察者模式的LiveData。 并且,ViewModel也不能持有UI層引用,因為ViewModel的生命周期更長。
所以,ViewModel不需要也不能 持有UI層引用,那么就避免了可能的內存泄漏,同時實現了解耦。這就解決了第二個問題。
## 原理
### ViewModelStore
```
**
* 用于存儲ViewModels.
* ViewModelStore實例 必須要能 在系統配置改變后 依然存在。
*/
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
Set<String> keys() {
return new HashSet<>(mMap.keySet());
}
/**
* 調用ViewModel的clear()方法,然后清除ViewModel
* 如果ViewModelStore的擁有者(Activity/Fragment)銷毀后不會重建,那么就需要調用此方法
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
```
### 配置更改重建后ViewModel依然存在
* 如果存儲器是空,就先嘗試 從lastNonConfigurationInstance從獲取
* mLastNonConfigurationInstances是在Activity的attach方法中賦值
* ActivityThread 中的 ActivityClientRecord 是不受 activity 重建的影響,那么ActivityClientRecord中lastNonConfigurationInstances也不受影響
# MVVM

**MVVM各職責如下**:
* Model模型:業務相關的數據(如網絡請求數據、本地數據庫數據等)及其對數據的處理
* View視圖:頁面視圖(Activity/Fragment),負責接收用戶輸入、發起數據請求及展示結果頁面
* ViewModel:M與V之間的橋梁,負責業務邏輯
**MVVM特點**:
* View層接收用戶操作,并通過持有的ViewModel去處理業務邏輯,請求數據;
* ViewModel層通過Model去獲取數據,然后Model又將最新的數據傳回ViewModel層,到這里,ViewModel與Presenter所做的事基本是一樣的。但是ViewModel不會也不能持有View層的引用,而是View層會通過觀察者模式監聽ViewModel層的數據變化,當有新數據時,View層能自動收到新數據并刷新界面。
#### UI驅動 vs 數據驅動
`MVP`中,`Presenter`中需要持有`View`層的引用,當數據變化時,需要主動調用`View`層對應的方法將數據傳過去并進行UI刷新,這種可以認為是UI驅動;而`MVVM`中,`ViewModel`并不會持有`View`層的引用,`View`層會監聽數據變化,當`ViewModel`中有數據更新時,`View`層能直接拿到新數據并完成UI更新,這種可以認為是數據驅動,顯然,`MVVM`相比于`MVP`來說更加解耦。
# MVI架構
本質就是`單向數據流動`+`狀態集中管理` (谷歌推薦的最佳實踐)
## 單向數據流動
新`UI State`主要分為以下幾步:
1. `ViewModel` 會存儲并公開`UI State`。`UI State`是經過`ViewModel`轉換的應用數據。
2. `UI`層會向`ViewModel`發送用戶事件通知。
3. `ViewModel`會處理用戶操作并更新`UI State`。
4. 更新后的狀態將反饋給`UI`以進行呈現。
5. 系統會對導致狀態更改的所有事件重復上述操作。

### 優勢
單向數據流動可以實現關注點分離原則,它可以將狀態變化來源位置、轉換位置以及最終使用位置進行分離。
這種分離可讓`UI`只發揮其名稱所表明的作用:通過觀察`UI State`變化來顯示頁面信息,并將用戶輸入傳遞給`ViewModel`以實現狀態刷新。
換句話說,單向數據流動有助于實現以下幾點:
1. 數據一致性。界面只有一個可信來源。
2. 可測試性。狀態來源是獨立的,因此可獨立于界面進行測試。
3. 可維護性。狀態的更改遵循明確定義的模式,即狀態更改是用戶事件及其數據拉取來源共同作用的結果。
## UIState 集中管理
### 缺點
* 不相關的數據類型:`UI`所需的某些狀態可能是完全相互獨立的。在此類情況下,將這些不同的狀態捆綁在一起的代價可能會超過其優勢,尤其是當其中某個狀態的更新頻率高于其他狀態的更新頻率時。
* `UiState diffing`:`UiState` 對象中的字段越多,數據流就越有可能因為其中一個字段被更新而發出。由于視圖沒有 `diffing` 機制來了解連續發出的數據流是否相同,因此每次發出都會導致視圖更新。當然,我們可以對 `LiveData` 或`Flow`使用 `distinctUntilChanged()` 等方法來實現局部刷新,從而解決這個問題
### UI State實現局部刷新
下面我們就來看下`LiveData`怎么實現屬性監聽
~~~kotlin
//監聽一個屬性
fun <T, A> LiveData<T>.observeState(
lifecycleOwner: LifecycleOwner,
prop1: KProperty1<T, A>,
action: (A) -> Unit
) {
this.map {
StateTuple1(prop1.get(it))
}.distinctUntilChanged().observe(lifecycleOwner) { (a) ->
action.invoke(a)
}
}
//監聽兩個屬性
fun <T, A, B> LiveData<T>.observeState(
lifecycleOwner: LifecycleOwner,
prop1: KProperty1<T, A>,
prop2: KProperty1<T, B>,
action: (A, B) -> Unit
) {
this.map {
StateTuple2(prop1.get(it), prop2.get(it))
}.distinctUntilChanged().observe(lifecycleOwner) { (a, b) ->
action.invoke(a, b)
}
}
internal data class StateTuple1<A>(val a: A)
internal data class StateTuple2<A, B>(val a: A, val b: B)
//更新State
fun <T> MutableLiveData<T>.setState(reducer: T.() -> T) {
this.value = this.value?.reducer()
}
~~~
1. 如上所示,主要是添加一個擴展方法,也是通過`distinctUntilChanged`來實現防抖
2. 如果需要監聽多個屬性,例如兩個屬性有其中一個變化了就觸發刷新,也支持傳入兩個屬性
3. 需要注意的是`LiveData`默認是不防抖的,這樣改造后就是防抖的了,所以傳入相同的值是不會回調的
4. 同時需要注意下承載`State`的數據類需要防混淆
# 參考資料
[Google 推薦使用 MVI 架構?卷起來了~](https://juejin.cn/post/7048980213811642382)
[“終于懂了“系列:Jetpack AAC完整解析(一)Lifecycle 完全掌握!](https://blog.csdn.net/hfy8971613/article/details/109641527)
[Android消息總線的演進之路:用LiveDataBus替代RxBus、EventBus](https://tech.meituan.com/2018/07/26/android-livedatabus.html)
- Android
- 四大組件
- Activity
- Fragment
- Service
- 序列化
- Handler
- Hander介紹
- MessageQueue詳細
- 啟動流程
- 系統啟動流程
- 應用啟動流程
- Activity啟動流程
- View
- view繪制
- view事件傳遞
- choreographer
- LayoutInflater
- UI渲染概念
- Binder
- Binder原理
- Binder最大數據
- Binder小結
- Android組件
- ListView原理
- RecyclerView原理
- SharePreferences
- AsyncTask
- Sqlite
- SQLCipher加密
- 遷移與修復
- Sqlite內核
- Sqlite優化v2
- sqlite索引
- sqlite之wal
- sqlite之鎖機制
- 網絡
- 基礎
- TCP
- HTTP
- HTTP1.1
- HTTP2.0
- HTTPS
- HTTP3.0
- HTTP進化圖
- HTTP小結
- 實踐
- 網絡優化
- Json
- ProtoBuffer
- 斷點續傳
- 性能
- 卡頓
- 卡頓監控
- ANR
- ANR監控
- 內存
- 內存問題與優化
- 圖片內存優化
- 線下內存監控
- 線上內存監控
- 啟動優化
- 死鎖監控
- 崩潰監控
- 包體積優化
- UI渲染優化
- UI常規優化
- I/O監控
- 電量監控
- 第三方框架
- 網絡框架
- Volley
- Okhttp
- 網絡框架n問
- OkHttp原理N問
- 設計模式
- EventBus
- Rxjava
- 圖片
- ImageWoker
- Gilde的優化
- APT
- 依賴注入
- APT
- ARouter
- ButterKnife
- MMKV
- Jetpack
- 協程
- MVI
- Startup
- DataBinder
- 黑科技
- hook
- 運行期Java-hook技術
- 編譯期hook
- ASM
- Transform增量編譯
- 運行期Native-hook技術
- 熱修復
- 插件化
- AAB
- Shadow
- 虛擬機
- 其他
- UI自動化
- JavaParser
- Android Line
- 編譯
- 疑難雜癥
- Android11滑動異常
- 方案
- 工業化
- 模塊化
- 隱私合規
- 動態化
- 項目管理
- 業務啟動優化
- 業務架構設計
- 性能優化case
- 性能優化-排查思路
- 性能優化-現有方案
- 登錄
- 搜索
- C++
- NDK入門
- 跨平臺
- H5
- Flutter
- Flutter 性能優化
- 數據跨平臺