Android Jetpack 架構元件最佳實踐
Android Jetpack 是一套元件、工具和指導,可以幫助您快速構建出色的 Android 應用。
-
Google在17年的I/O大會上推出了架構元件(Architecture Component)工具集。
-
隨後在18年I/O大會上釋出了 Android Jetpack ,Jetpack 是Android開發元件工具集,旨在幫助我們輕鬆構建更穩定、更健壯、以及更可維護的應用程式。
每個 Jetpack 元件均可單獨採用,並且它們依然可以流暢地協作。
Jetpack主要分為4個部分, 基礎、架構、行為、介面 。從圖中得知,Jetpack並不全是些新東西,只要是能夠幫助開發者更好更方便地構建應用程式的元件,Google都將其歸納入了Jetpack,可以看出Google對jetpack很重視,對開發者很上心。
-
緊接著Google推出 AndroidX ,將許多Google認為是正確的方案和實踐集中起來。
- AndroidX 是對support library的重大改進。
- AndroidX中的所有軟體包名都以字串androidx.開頭,位於一致的名稱空間中。
- 與support支援庫不同,AndroidX軟體包可單獨維護和更新。
- 所有新的支援庫開發都將在AndroidX庫中進行。
官方推薦架構
使用此架構能帶來什麼好處?
-
UI和業務邏輯解耦。
-
有效避免生命週期元件記憶體洩漏。
- 提高模組可測試性。
- 提高應用穩定性,有效降低以下異常發生概率。
- Can not perform this action after onSaveInstanceState
- WindowManager$BadTokenException, is your activity running?
- OOM 、 NullPointerException
測試每個元件
-
介面和互動:使用 Android 介面插樁測試 。基於此架構只需mock 一個
ViewModel
即可完成介面測試。 -
ViewModel:使用 JUnit 測試 。只需mock一個類,即
Repository
。 -
Repository:使用 JUnit 測試 。只需mock兩個類,XxxDao,XxxService;由於XxxDao,XxxService都是介面,還可以建立虛擬實現來完成複雜測試用例。
-
XxxDao:可以使用插樁測試來測試 DAO 類。這裡注意對於每個測試,都請建立記憶體中資料庫以確保測試沒有任何副作用(例如更改磁碟上的資料庫檔案)。
-
XxxService:就Retrofit而言可以使用 MockWebServer 模擬本地伺服器。
Lifecycle
Lifecycle是一個類,它包含元件(Activity或Fragment)生命週期狀態的資訊,並允許其他物件觀察此狀態。
跟蹤元件生命週期
- Lifecycle內部使用兩個主要列舉( Event 、 State )來跟蹤其關聯元件的生命週期狀態。
- Event :對應Activity或Fragment元件的生命週期回撥事件。
- State :表示被跟蹤元件的當前狀態,其中
STARTED
和RESUMED
為活躍狀態,可接受到liveData的資料更新。
LifecycleOwner和LifecycleRegistry
- LifecycleOwner : 是一個單一的方法介面,表示該類具有生命週期。 support包從26.1.0版本開始,Fragment和Activity就預設實現了該介面。
- LifecycleRegistry : Lifecycle介面的實現類,協助元件處理生命週期,可處理多個觀察者。如果你想自定義LifecyclerOwner請參考support包中Fragment和Activity實現。
ViewModel
ViewModel 是用來儲存應用UI資料的類,它會在配置變更(Configuration Change)後繼續存在。
生命週期
關於ViewModel的生命週期就一句話: 在Activity/Fragment等元件整個生命週期過程中,ViewModel的例項有且只有一個。
這樣設計好處在哪呢?
- 可用ViewModel儲存資料,它能安全度過手機旋轉等配置變更場景。
- ViewModel能很好的實現多個Fragment之間的資料共享。
單一責任原則
上圖為官方中文視訊截圖。
- Actvity或Fragment只顯示UI和接收互動。
- 為避免ViewModel臃腫,可建立presenter處理UI資料。(比如從資料列表中獲取某個item的屬性)
- Repository 資料來源操作入口。(便於單元測試)
- 配合其它架構元件使用。
最佳實踐
- 如何時候都不要將Context傳入ViewModel。
- 如果要在ViewModel中使用Application例項,請使用AndroidViewModel子類。
- ViewModel+LiveData+Databinding 可構建反應式UI。(請檢視文末提供的參考資料)
-
ViewModel與onSaveInstanceState要配合使用。
ViewModel | onSaveInstanceState
—|—
能度過配置變更 | 能度過配置變更和程序關閉
儲存大量資料 | 儲存少量資料
| 可序列化
ViewModel和onSaveInstanceState是相輔相成的,當程序被關閉時,ViewModel會被銷燬,而onSaveInstanceState不會受影響。所以用onSaveInstanceState儲存少量關鍵資料(如xxxId),並在該場景恢復後用來載入頁面資料。
ViewModel和View之間通訊
- UserProfileActivity引用UserViewModel,可觀察其提供的UserLiveData、StatusLiveData、PageStateLiveData資料來源變更來重新整理UI。
- 響應使用者事件,比如更新使用者資訊。Activity將更新事件傳遞給ViewModel,ViewModel有將其委託給Presenter處理,Presenter處理過程及結果通過LiveData與Activity互動。
- 注意Activity和ViewModel之間是單向引用。 為避免記憶體洩漏,ViewModel不能持有任何Context引用。
LiveData
LiveData是一個具有生命週期感知特性的可觀察的資料保持類。
- LiveData只通知活躍狀態(
STARTED
orRESUMED
)的Observer更新,並在DESTROYED
狀態時自動移除Observer,避免記憶體洩漏。 - 始終保持最新資料。舉例:1.退後臺的Activity在返回前臺後會立即收到最新資料。2. 配置變更導致Activity重建後也會立即收到最新資料。
- 共享資源。單利模式共享同一個LiveData。
LiveData、MutableLiveData、MediatorLiveData區別?
- 繼承關係:MediatorLiveData -> MutableLiveData -> LiveData。 所以MediatorLiveData功能最強大。
- LiveData 是一個具有生命週期感知的可觀察的資料保持類。
- MutableLiveData 在LiveData基礎上打開了修改Value的方法許可權。
- MediatorLiveData 可管理多個LiveData。
Transformations
-
map: 將一種資料型別的
轉換為另一種型別 ```LiveData ```// 觀察將被轉換LiveData,待其資料來源變更後轉換為LiveData 並通知訂閱者。
// 內部使用的MediatorLiveData實現。
LiveData
userLiveData = …; LiveData<String> userName = Transformations.map(userLiveData, user -> { user.name + " " + user.lastName
});
- **switchMap** : 和map類似。差別在於triggerLiveData變更後,會觸發和等待另外一個LiveData獲取資料。
// 例項程式碼:將addressInputLiveData轉換為postalCodeLiveData.
class MyViewModel extends ViewModel {
private final PostalCodeRepository repository;
private final MutableLiveDataaddressInput = new MutableLiveData();
public final LiveData
postalCode = Transformations.switchMap(addressInput, (address) -> { return repository.getPostCode(address); }); public MyViewModel(PostalCodeRepository repository) { this.repository = repository }
// addressInputLiveData變更時觸發repository.getPostCode,
// 待其回去成功後,再將資料設定給postalCodeLiveData。
private void setInput(String address) { addressInput.setValue(address); }
}
```
幾個問題
LifecycleOwner元件是如何與liveData通訊的?
- SupportActivity 通過新增一個空的ReportFragment來處理生命週期狀態變更回撥;Fragment則在自身生命週期函式中處理。
- LifecycleOwner元件,通過LifecycleRegistry類中handleLifecycleEvent -> dispatchEvent方法與liveData通訊,從而是liveData具有自動感知元件生命週期的能力。
- 元件銷燬時,LifecycleRegistry會通知liveData移除observer。
ViewModel如何做到一直在記憶體中,直到Activity銷燬或Fragment被移除時才被清除的?
1.x.x版本實現
- Activity或Fragment會新增一個空的HolderFragment,而ViewModelStore例項被HolderFragment持有,所以就保證了整個生命週期中ViewModelStore例項始終唯一,也就保證了其快取的ViewModel例項會一直存在直到元件銷燬(在onDestroy中會呼叫ViewModelStore.clear()方法清除其快取的ViewModel例項)。
- 由於這個HolderFragment設定了setRetainInstance(true), 這樣在Activity重建時它不會執行onDestroy回撥,這就是它能度過配置變更的原因 。