ViewModel是Android Architecture Components中提供的一個組件,其做用是爲UI組件提供須要展示的數據內容,幫助開發者更優雅簡單高效的實現多個組件間的數據共享。其中值得注意的是ViewModel的生命週期,以往咱們將UI展現的數據直接緩存在對應的UI組件中,遇到ConfigurationChange等事件UI組件從新建立,咱們緩存的數據也隨之銷燬。但ViewModel能夠在內存中長期被持有而不受ConfigurationChange的影響,直到相關聯的UI組件真正銷燬的時候ViewModel才隨之釋放。下面是來自官方的對ViewModel生命週期的描述圖:html
本文主要探討文章標題提到的問題,關於ViewModel的細節介紹能夠參閱官方文檔。java
若是堆代碼不如本身去閱讀源碼,那樣反而來的更清晰直觀。因此後面的部分不會打源碼戰術,可是也有一些我的分析的過程描述,若是隻關心黑科技是啥,能夠直接跳到最後結論部分。android
由官方的demo咱們能夠看到使用ViewModel的簡單方法以下:git
public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<Users>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same MyViewModel instance created by the first activity.
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// update UI
});
}
}
複製代碼
咱們須要實現一個派生自ViewModel的類。接着在須要的UI組件中經過ViewModelProviders.of().get()來獲取咱們定義的ViewModel實例。demo中看到真正使用時每每在ViewModel中以LiveData來封裝須要UI組件展示的數據,因而在獲取咱們定義的ViewModel實例後即可以經過其內部的LiveData來添加觀察者,進而實現數據變動時能及時的通知到UI組件。關於LiveData的細節介紹能夠參閱官方文檔。 咱們仍是回到ViewModel。就以demo中看到的ViewModelProviders爲切入點。github
我畫了一張類圖,打算嘗試按照本身的理解將他們的關係講清楚,那也就基本瞭解爲何ViewModel對ConfigurationChange免疫了。也爲你們查閱源碼或者提出異議提供一個思路。緩存
查看原圖圖中虛線箭頭表示依賴關係,箭頭尾部對應的類依賴箭頭頭部對應的類。 中間的實線箭頭表示類的組合關係,箭頭尾部對應的類中包含箭頭頭部對應的類的實例。app
ViewModel對ConfigurationChange免疫,換個角度能夠理解爲在不一樣的UI組件實例中獲取的ViewModel實例相同。 根據demo得知,咱們經過ViewModelProviders.of().get()這樣一個鏈式調用來獲取ViewModel實例。 因此要得到一個ViewModel分爲兩個步驟:async
第一步,調用ViewModelProviders.of()方法獲取一個ViewModelProvider實例。這正是圖中左上部分表達的信息:ide
ViewModelProvider依賴於ViewModelProviders,ViewModelProvider是經過ViewModelProviders提供的一系列of方法構造出來的。經過ViewModelProvider的構造方法的定義能夠看到在構造時須要傳入ViewModelStore和Factory。ViewModelStore是真正緩存ViewModel的地方,Factory則用於在緩存未命中時建立ViewModel。fetch
第二步,經過ViewModelProvider.get()方法獲取ViewModel實例,這從圖中的ViewModelProvider和ViewModelStore的關係能有所體現:
ViewModelProvider內部有一個ViewModelStore類型的成員變量mViewModelStore和一個用於建立ViewModel的Factory成員變量mFactory。他們是在建立對象時經過構造方法傳入的。調用ViewModelProvider.get()方法實際上是經過內部的ViewModelStore.get()方法來獲取ViewModel,而若是獲取爲空,則經過mFactory構建一個新的ViewModel。
ViewModelStore中保存一個Map鍵值對,這裏是真正緩存ViewModel的地方,因此ViewModelProvider.get()最終會調到ViewModelStore的這個Map中來找緩存的ViewModel。 由於最終是從ViewModelStore的Map鍵值對中查找緩存的ViewModel,因此要保證ViewModel的惟一性其實就是保證ViewModelStore的惟一性。 前面提到ViewModelProvider中的mViewModelStore是先經過ViewModelStores構造好,而後利用其構造方法傳遞到ViewModelProvider中的。因此接下來的重點就是ViewModelStores如何構造一個ViewModelStore。
ViewModelStore經過ViewModelStores構造,其構造過程須要圖中的ViewModelStores和HolderFragment類一塊兒完成。 從圖中能夠看到ViewModelStore是經過ViewModelStores.of()方法來構造的,ViewModelStores.of()方法內部會直接調用HolderFragment的靜態方法holderFragmentFor(),該靜態方法返回一個HolderFragment對象,這個HolderFragment對象就是一個派生自Fragment的類,只不過它不包含任何View樹結構。接着調用它的getViewModelStore()方法返回HolderFragment的成員變量mViewModelStore,這個mViewModelStore是做爲成員變量在HolderFragment類加載時直接new出來的。因此一個HolderFragment對象必定對應惟一一個ViewModelStore對象實例。同時這個mViewModelStore實例會做爲前面構造ViewModelProvider時的參數傳進去。也就是說ViewModelProvider和HolderFragment中的mViewModelStore對象指向的是同一個實例,這個實例是先在HolderFragment中構造初始化以後傳遞到ViewModelProvider中去的。 既然ViewModelStore是經過HolderFragment建立出來的,一個HolderFragment實例內部惟一對應一個ViewModelStore實例,那如今的問題就變成如何保證HolderFragment的惟一性了。 到這裏,黑科技終於登場。其實就是利用Fragment的setRetainInstance()方法。經過在實例化HolderFragment的時候調用該方法,並傳入true做爲參數。HolderFragment就能夠在其宿主的Fragment或Activity重走生命週期時在內存中持久化其自身實例,即此時Fragment對應的View會從View樹中移除,可是Fragment實例自己不會銷燬,在下一次宿主Fragment或Activity重走生命週期時會複用內存中持久化的Fragment實例。以此來達到HolderFragment實例的惟一性。關於setRetainInstance()方法的使用詳情能夠參閱官方文檔。