Jetpack的分類
1. DataBinding:以聲明方式將可觀察數據綁定到界面元素,通常和ViewModel配合使用。
2. Lifecycle:用于管理Activity和Fragment的生命周期,可幫助開發者生成更易于維護的輕量級代碼。
3. LiveData: 在底層數據庫更改時通知視圖。它是一個可觀察的數據持有者,與常規observable不同,LiveData是生命周期感知的。
4. Navigation:處理應用內導航。
5. Paging:可以幫助開發者一次加載和顯示小塊數據,按需加載部分數據可減少網絡帶寬和系統資源的使用。
6. Room:友好、流暢的訪問SQLite數據庫。它在SQLite的基礎上提供了一個抽象層,允許更強大的數據庫訪問。
7. ViewModel: 以生命周期的方式管理界面相關的數據,通常和DataBinding配合使用,為開發者實現MVVM架構提供了強有力的支持。
8. WorkManager: 管理Android的后臺的作業,即使應用程序退出或設備重新啟動也可以運行可延遲的異步任務。
Android 標準架構框架圖:
Android官方架構部分的知識 https://developer.android.google.cn/topic/architecture/intro?hl=zh-cn
ViewBinding&DataBinding篇
ViewBinding介紹和使用
大家還是不要把ViewBinding和DataBinding這兩個不要混淆了.
ViewBindling 這是一個負責綁定View到代碼,減少 findViewId降低空引用資源ID錯誤。使用起來也較為簡單, 流程是:
第一、 在 app模塊下的build.gradle.kts下,打開使用ViewBinder的開關:
Android{
…
buildFeatures {ViewBinding = true}
}
第二就是需要在使用的Activity、fragment、view里面去初始化一下,inflate“
class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)}
}
第三步就可以直接引用使用了
binding.button.text="helloworld"
注意: viewBinding 目前能夠支持所有Xml的控件進行自動綁定, 包含Activity、Fragment以及其他View等。
ViewBinding原理
開啟后會自動生成【xml的文件名 自動大駝峰】+Binding.kt 文件。例如ActivityMainBinding.kt。可以看一下自動生成文件代碼,我只看inflate最后執行的部分:
public static ActivityMainBinding bind(@NonNull View rootView) {
int id;
id = R.id.tv1;
TextView tv1 = ViewBindings.findChildViewById(rootView, id);if (tv1 == null) {
break missingId;
}
id = R.id.tv2;
TextView tv2 = ViewBindings.findChildViewById(rootView, id);
if (tv2 == null) {
break missingId;
}
return new ActivityMainBinding((ConstraintLayout) rootView, tv1, tv2);}
}
可以看到自動生成的代碼里,會把XML里控件的每個id都會塞進去,并且調findViewById進行綁定。
DataBinding的介紹和使用
Android 開發中體現 MVVM 架構思想的 Data Binding,其核心是 觀察者模式 的特定實現。首先,它有三個主要的實體:
- Data:與 View 相關的數據,它可以是 View 的可觀察者對象;
- View:展示給用戶的視圖,如果有交互功能且能更新數據,它可以是 Data 的可觀察者對象;
- ViewDataBinding:連接 Data 和 View 的中介,當 Data 或 View 作為可觀察者對象時,它充當可觀察者對象的代理。假如當我們寫了一個名為 demo.xml 的 Data Binding 的 layout 文件后,編譯工具會生成一個相應的類——DemoBinding,它的原型就是 ViewDataBinding。我們通常通過 DataBindingUtil.inflate(inflater, R.layout.demo, container, false) 來實例化的 DemoBinding 對象,即 ViewDataBinding。
主要是三個方面的功能:
? 將特定的 View 與特定的 Data 進行綁定,便于模塊化;
? View 自動感知和響應 Data 的變化,使得處理數據的業務層不必關心 View 的狀態,便于解耦;
? Data 也可以自動同步帶有交互功能的 View 對數據的修改,使得 UI 層的交互不必擔心數據是否能同步 View 狀態的問題,仍然便于解耦
使用類似ViewBinding:
第一是,在 app模塊下的build.gradle.kts下,打開使用ViewBinder的開關:
Android{
…
buildFeatures {DataBinding = true}
}
第二就可以直接在需要使用Xml文件里使用就行了,但是前提現有已經新簡的Data類:
class User(var firstName: String, var lastName: String) {}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"><data> <variable name="user" type="com.example.User"/></data> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.firstName}"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.lastName}"/> </LinearLayout></layout>
基礎用法很簡單, 但是這只是單項View-Data綁定。
比較好的功能是 它還能幫助我們做到通過感知Data的局部變量來進行局部刷新,還有可以可以綁定View,進行雙向綁定。使用方法就是將Data轉化成observe 對象,并且添加@Bindable 的竹節,通過notifyPropertyChanged()方法來達成局部刷新,如下:
public class DemoData extends BaseObservable {private int element;
// 定義其它成員,省略
@Bindablepublic int getElement() {return this.element;}
public void setElement(int e) {this.element = e;notifyPropertyChanged(BR.element);}
// 省略其它成員操作
}
ViewModel篇
viewModel 的使用, viewModel繼承子類使用, 數據存儲在內存中,可以和acitivty 生命周期進行綁定,也獨立于Activity;onSaveInstanceState(Bundle) 存儲的數據,僅在當前應用進程哪有效, 當退出重新進入, Bundle重新恢復成初始狀態。或者在 OnStop中保存永久性數據
存儲作用域
實例化 ViewModel 時,您會向其傳遞實現 ViewModelStoreOwner 接口的對象。它可能是 Navigation 目的地、Navigation 圖表、activity、fragment 或實現接口的任何其他類型。然后,ViewModel 的作用域將限定為 ViewModelStoreOwner 的 Lifecycle。它會一直保留在內存中,直到其 ViewModelStoreOwner 永久消失。
有一系列類是 ViewModelStoreOwner 接口的直接或間接子類。直接子類為 ComponentActivity、Fragment 和 NavBackStackEntry。如需查看間接子類的完整列表,請參閱 ViewModelStoreOwner 參考文檔。
當 ViewModel 的作用域 fragment 或 activity 被銷毀時,異步工作會在作用域限定到該 fragment 或 activity 的 ViewModel 中繼續進行。這是持久性的關鍵。
如需了解詳情,請參閱下文有關 ViewModel 生命周期的部分
我這里不去贅述 如何使用它, 我曾在上一家公司中對的Alios系統平臺, 使用TS語言設計了一個跟viewModel類似的 組件庫DataManager .主要的設計原則是觀察者綁定和職責分離原則。 本質上來講ViewModel 就是一個獨立的數據儲存類, 設計者在里面做了跟Activity的destory來消亡的【非因為配置改變導致的destory】,其實也可以做到完全獨立,消亡由開發者自己決定, 這樣就會很有趣了,多個activty可以共一個ViewModel實例.只是里面做了一些對象傳遞的封裝,就可以和LiveData進行配合使用。
- ViewModelStore:用于存儲ViewModel實例的類,內部持有一個HashMap保存實例,ViewModelProvider會將創建好的ViewModel實例保存到ViewModelStore中,之后再需要此類ViewModel的實例時就直接從中讀取。
- ViewModelProvider.Factory:前文已經提到,這是用于創建ViewModel實例的工廠,ViewModelProvider當需要ViewModel的實例又在ViewModelStore中沒有找到對應實例時就會調用工廠的create方法創建。
- CreationExtras:前文也已提到,它用于在創建ViewModel實例時從外界向構造過程傳遞參數,內部持有一個MutableMap,以key-value的形式存儲和查找參數
ViewModelStore中的HashMap 設計應該是至少兩層以上的,在當初我設計的空間中,這里用的兩層, 并且因為是量產項目的原因, 加了一個些狀態機key值進行了耦合。 Android官方是直接保存的ViewModel的對象,這樣就只有一層了。
每個Acitivty都會有一個ViewModelStoreOwner ,這樣在開發者做項目的時候,就不需要關系每個Activity的獲取ViewModel的時候,就是綁定的哪一個。
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
…
// 實現了 ViewModelStoreOwner 接口
ViewModelStoreOwner,
…{
private ViewModelStore mViewModelStore;
// 重寫了 ViewModelStoreOwner 接口的唯一的方法 getViewModelStore()
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ “Application instance. You can’t request ViewModel before onCreate call.”);
}
ensureViewModelStore();
return mViewModelStore;
}
ViewModelProvider 的構造方法時 ,獲取 ViewModelStore 對象時,實際調用了 MainActivity#getViewModelStore() ,而 getViewModelStore() 實現在 MainActivity 的父類 ComponentActivity 中。
在返回 mViewModelStore 對象之前調用了 ensureViewModelStore()
void ensureViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
- onRetainNonConfigurationInstance 方法和 getLastNonConfigurationInstance 是成對出現的,跟 onSaveInstanceState 機制類似,只不過它是僅用作處理配置更改的優化。
- 返回的是 onRetainNonConfigurationInstance 返回的對象Activity 在因配置更改而銷毀重建過程中會先調用 onRetainNonConfigurationInstance 保存 viewModelStore 實例。 在重建后可以通過 getLastNonConfigurationInstance 方法獲取之前的 viewModelStore 實例。
另外,我們可以看一下 mViewModelStore ,銷毀的時機,其實是在生命周期的判斷Lifecycle.Event.ON_DESTROY 被摧毀并且 判斷是否是配置改變引起的isChangingConfigurations()來進行區分:
getLifecycle().addObserver(new LifecycleEventObserver() {@Overridepublic void onStateChanged(@NonNull LifecycleOwner source,@NonNull Lifecycle.Event event) {if (event == Lifecycle.Event.ON_DESTROY) {// Clear out the available contextmContextAwareHelper.clearAvailableContext();// And clear the ViewModelStoreif (!isChangingConfigurations()) {getViewModelStore().clear();}}}});...}
ViewModel 出現之前,Activity 可以使用 onSaveInstanceState() 方法保存,然后從 onCreate() 中的 Bundle 恢復數據,但此方法僅適合可以序列化再反序列化的少量數據(IPC 對 Bundle 有 1M 的限制),而不適合數量可能較大的數據,如用戶信息列表或位圖。 ViewModel 的出現完美解決這個問題。
LiveData篇
通常情況下LiveData都是配合viewModel使用,在某個具體的ViewModel類中定義LiveData數據,然后在對應的Activity或Fragment中觀察LiveData數據的變化,LiveData的使用使得我們不再將數據保存在Activity或Fragment中,減輕了Activity或Fragment的工作量,使得Activity或Fragment只負責界面的管理和顯示,而不在保存數據也不會受到數據的影響。但是也可以使用oberveForever()不綁定生命周期, 那就需要自己手動管理它的消亡。
核心方法
通過 observe(owner,observer) 向 LiveData 注冊觀察者
? 通過 observe(owner,observer) 向 LiveData 注冊觀察者,并且把 observer 包裝成一個 LifecycleBoundObserver,它是一個具有生命周期邊界的觀察者,因為這個觀察者只有當宿主處于 STARTED 或者 RESUMED 狀態的它才會接收數據,其他時候它是不會接收數據的。
? 把包裝好的 Observer 注冊到 Lifecycle 當中,handlerLifecycleEvent(event) 利用 Lifecycle 能力,它能感知宿主生命周期能力的關鍵地方。注冊時和宿主每次生命周期變化都會回調 onStateChanged() 方法,剛進去的時候會觸發方法的同步。
? 會判斷這個事件宿主是否被銷毀了,從而主動地把 Observer 從 LiveData 中移除掉,流程結束。如果不是 DESTORY,說明宿主當前的狀態發生了變化,它會觸發 activeStateChanged(boolean newActive) 方法,它會判斷當前 Observer 是否處于活躍的狀態,如果宿主的狀態為 STARTED,RESUMED 則會分發最新數據到每個觀察者。
? 進而調用 dispatchingValue(ObserverWrapper) 分發數據,如果 ObserverWrapper 為空則分發數據給 liveData 中存儲的所有觀察者,如果不為空,則分發數據給該 Observer。
? considerNotify(ObserverWrapper) 中先判斷觀察者所在的宿主不活躍,則不分發;接著如果 observer 的 mLastVersion 大于或等于 LiveData 的 mVersion 則不分發,防止重復發送數據;最后通過 observer.mObserver.onChanged((T) mData) 分發數據,同步 mVersion 數據。
? 那么 LiveData 先發送數據,后注冊的 Observer 能接收到數據嗎? 答案是可以的。
普通消息的發送
? postValue() 發送一條數據,它可以在任意線程使用的,里面實際使用了 Handler.post 先把這個事件發送到主線程,然后在調用 setValue() 發送數據;
? setValue() 代表著 LiveData 發送數據,每發送一次 mVersion++,另外 LifecycleBoundObserver 中也有一個,它代表這個 Observer 接收了幾次數據,在分發數據的時候,這兩個 version 會進行比對,防止數據重復發送;
? setValue() 里面也會觸發 dispatchingValue(ObserverWrapper),ObserverWrapper 為 null,dispatchingValue() 它會遍歷 Observer 集合里面所有觀察者,然后逐一調用 considerNotify(ObserverWrapper) 去做消息的分發。
此外,LiveData和 Room配合使用 時候,
LiveData 就是為了簡化room 的Dao查詢對象到 viewModel的過程, 返回LiveData對象, 查詢數據的操作就不需要自己去創建后臺線程 。 LiveData能夠自動完成。至于為什么Room的查詢使用LiveData對象就不需要自己起子線程。 是因為 當liveData做返回對象的時候,room的 查詢方法,會多使用一個ComputableLiveData類, 這里面會判斷是否有子線程,如果沒有,自己會自動起一個子線程去查詢,普通類型就會直接走SupportSQLiteDatabase#query(SupportSQLiteQuery)方法,看下面代碼:
并且使用LiveData.observe.能夠添加觀察者, 綁定fragment周期和數據的更新,只要LiveData的數據發生變化, 就會實時刷新:
普通數據類型的查詢:
/*** Wrapper for {@link SupportSQLiteDatabase#query(SupportSQLiteQuery)}.** @param query The Query which includes the SQL and a bind callback for bind arguments.* @return Result of the query.*/public Cursor query(SupportSQLiteQuery query) {assertNotMainThread();return mOpenHelper.getWritableDatabase().query(query);}
使用LiveData作為返回值時用到了ComputableLiveData類,此類在構造的時候就將RoomDatabase中的mQueryExecutor傳入了。如果在構造的時候沒有傳入自定義的Executor,那么會自動生成一個。會走這個方法:
if (mQueryExecutor == null) {mQueryExecutor = ArchTaskExecutor.getIOThreadExecutor();}