Android Jetpack之ViewModel

概述

ViewModel仍然是Model的範疇,是數據對象的載體,可是多了與視圖(View)生命週期的綁定關係。能夠簡單理解爲帶有生命週期的數據對象。可在Activity, Fragment中使用,保證其在生命週期內的惟一性和一致性,不受配置的更改(例如屏幕旋轉)。
android

使用場景

  • 某頁面屏幕旋轉致使Activity/ Fragment銷燬重建數據不能保存。
    你可能會說我用onSaveInstanceState保存下來,而後某onCreate, onRestoreInstanceState還原啊。onSaveInstanceState設計用來存儲那些小的與 UI 相關的而且序列化或者反序列化不復雜的數據。若是被序列化的對象複雜的話,序列化會消耗大量的內存。因爲這一過程發生在主線程的配置更改期間,它須要快速處理纔不會丟幀和引發視覺上的卡頓。
  • 仍然是屏幕旋轉問題,通常頁面重建後須要從新請求數據,若是能緩存下來是否是就能減小一次請求?又或者靜置後臺一小會回來用onSaveInstance實現就略麻煩了。
  • Activity+ Fragments時單個Fragment但願主動拿到Activity的數據完成渲染。此場景換成Activity/ Fragment+自定義View也是同樣的。
    你可能又說我讓Activity實現個接口,Fragment中將context對象轉成接口不就能拿到Activity的數據了嘛。
    我想說還得定義接口,有沒有不那麼麻煩的方法;另外若是不恰當地調用,Activity被銷燬了還會出現空指針啊。
  • 想象某Activity+ Fragments須要Fragment之間通訊,甚至某Fragment的更改影響到其餘Fragment。
    估計你們的第一反應是讓Activity當中介或者用Eventbus這種去通知吧

    總之,學習了ViewModel你會發現一種新思路去解決這些問題。既能保持數據在生命週期內的惟一性又不須要考慮由於異步操做致使的空指針。

ViewModel生命週期

下圖能夠看出,ViewModel的生命週期和Activity/ Fragment同樣甚至更長,由於若是發生屏幕旋轉,ViewModel並無跟着銷燬,直到最終的onDestroy。面試

圖1.ViewModel生命週期

ViewModel的使用

1.gradle依賴緩存

dependencies {
    ...
    implementation 'android.arch.lifecycle:extensions:1.1.1'
}
複製代碼

2.ViewModel的聲明 千萬不能持有Context的引用,不然會引發內存泄漏,若是實在須要Context,能夠繼承AndroidViewModel,經過getApplication()獲取Application。bash

public class CustomModel extends ViewModel {
      public CustomObj obj;//業務內須要使用的對象
}
複製代碼

3.ViewModel的使用
微信

public class MyActivity extends Activity {
      public void myFunction() {
              //也能夠ViewModelProviders.of(MyActivity.this).get("key", CustomModel.class);
              CustomModel customModel = ViewModelProviders.of(MyActivity.this).get(CustomModel.class);
              customModel.obj...//本身操做該對象
      }
}
複製代碼

4.ViewModel與LiveData結合架構

public class CustomModel extends ViewModel {
      //業務內須要使用的變量
      public MutableLiveData<string> value = new MutableLiveData&lt;&gt;();
}
複製代碼
public class MyFragment1 extends Fragment {
      public void myFunction() {
              CustomModel customModel = ViewModelProviders.of(getActivity()).get(CustomModel.class);
              customModel.value.observe(MyFragment.this, new Observer<string>() {
                    @Override
                    public void onChanged(String value) {
                        //本身操做該參數,如textView.setText(value)
                    }
              });
      }
}
複製代碼
public class MyFragment2 extends Fragment {
      public void myFunction() {
              CustomModel customModel = ViewModelProviders.of(getActivity()).get(CustomModel.class);
              customModel.value.setValue("fragment2 changed the value");//注意postValue
      }
}
複製代碼

這樣兩個Fragment就能夠通訊了,LiveData的引入也能實時監聽數據的變化反應在界面上。

好了,ViewModel的基本用法掌握了,能夠開心地在簡歷上寫熟練掌握Android Jetpack之ViewModel。可是若是在面試官問「你知道它底層是怎麼實現的嗎」時,你不想一臉懵逼的說我猜。。。可能。。。大概。。。是這樣吧,反正做爲一個熱愛鑽研的攻城師的我是看了一遍源碼的。app

ViewModel實現原理

猜猜怎麼實現

1.既然能夠存儲多個不一樣的ViewModel,那麼它們是怎麼存儲的?
2.ViewModel數據保存在哪裏?Application級別仍是Activity/ Fragment級別?
3.怎麼保證ViewModel在配置更改(如屏幕旋轉)時不被銷燬?
4.怎麼保證Activity/ Fragment onDestroy時回收?
5.傳遞的是ViewModel.class,怎麼獲取實例?
6.AndroidViewModel持有的Application是在哪裏設置進去的?異步

原理分析

下面會一步步分析實現原理,着急的同窗請跳到最後直接看答案。 說明:androidx轉換先後對於ViewModel存儲方式有變化,這裏咱們分別聊下這兩種實現:ide

androidx轉換前

圖2.ViewModel的實現類圖
(圖片來源: blog.csdn.net/zhuzp_blog/…)

  • ViewModel, AndroidViewModel是咱們能夠繼承的類,後者持有Application對象。
  • ViewModelStore是存儲ViewModel的類,用HashMap存儲。
  • ViewModelProviders提供獲取ViewModelProvider的方法,支持activity, Fragment兩種傳值
  • ViewModelProvider組合VIewModelStore和Factory,可根據ViewModel的名稱獲取其實例。Factory有默認實現,也可自定義,實例的首次建立就是經過Factory反射獲得的。
  • HolderFragment是關鍵邏輯,實現了ViewModelStore的保存功能。本質上是Activity/ Fragment中插入的空Fragment,可以在配置發生更改時不被銷燬。
ViewModelProviders.of(activity/ fragment)

圖3.獲取ViewModelProvider
圖4.檢查並獲得Application

checkXXX方法校驗合法性同時獲取上層Context,最終得到Application對象;不自定義Factory的狀況下會使用系統的AndroidViewModelFactory,getInstance對象獲得static對象,它的做用是在ViewModel實例不存在時建立一個新的。
圖5.利用AndroidViewModelFactory建立ViewModel實例

回頭看圖3ViewModelProvider構造方法(具體見下面圖12)的第一個參數是ViewModelStores.of(fragment),原來是爲了獲得ViewModelStore,咱們先看下ViewModelStore的定義。簡單來講它是ViewModel的存儲工廠,底層用HashMap實現,還對外提供了clear方法。
圖6.ViewModelStore類

明白了ViewModelStore是個關鍵的存儲類,那麼它怎麼得到的?到底存在哪裏呢?
圖7.ViewModelStores類

先來看下ViewModelStoreOwner是什麼,顧名思義是存儲ViewModelStore的類,正常Activity/ Fragment通常不存儲,那麼往下看holderFragmentFor(fragment)就是關鍵實現了。圖8顯示該方法是static類型,目的是獲得HolderFragment。看來ViewModelStore是存儲在HolderFragment中了,那麼HolderFragment又是什麼東東??
圖9.得到HolderFragment

圖9.HolderFragment類

原來HolderFragment是一個Fragment,存儲着ViewModelStore,在onDestroy的時候調用ViewModelStore.clear方法。注意構造方法中setRetainInstance(true), 這個方法能讓Activity在配置更改重建時Fragment不被銷燬(具體功能自行查閱),也就是這個功能成爲屏幕旋轉ViewModel仍然保留的功臣。
圖10.HolderFragmentManager類

HolderFragmentManager有倆HashMap分別存Activity與插入的HolderFragment,Fragment和插入的子HolderFragment的鍵值對,並能監聽Activity/ Fragment的生命週期,在destroy時去掉緩存HolderFragment。下圖11仍然是HolderFragmentManager的方法,描述瞭如何建立HolderFragment,若是查找HolderFragment。
圖11.HolderFragmentManager類2

回過頭和圖9中建立HolderFragment聯繫在一塊兒看,原來是先在當前Activity/ Fragment中查找是否存在HolderFragment,不存在則在HolderFragmentManager的HashMap中找,實在沒有就建立一個放在Activity/ Fragment中。獲得HolderFragment就能獲得ViewModelStore,接着就有了ViewModelProvider,那麼接下來怎麼獲得ViewModel實例呢?

ViewModelProvider.get(ViewModel.class)

圖12.獲取ViewModel實例

獲取實例的方法很簡單,從ViewModelStore中查找,有則直接返回,沒有則經過反射建立一個(mFactory的建立過程見AndroidViewModelFactory)。至此,咱們就找到了咱們的主角ViewModel。
總結思想就是在Activity/ Fragment中插入一個惟一的HolderFragment來保存ViewModelStore,HolderFragment沒有視圖信息,不加入到view container中,經過setRetainInstance(true)保證配置更改不被銷燬,可是在onDestroy時手動調用ViewModelStore.clear方法釋放掉Map中全部的ViewModel對象。因爲沒有主動從載體中清除,HolderFragment自己就能和外層載體生命週期同樣長,同時setRetainInstance(true)保證了即便屏幕旋轉後載體重建也能不被銷燬,也能夠說它的生命週期比載體還長,從而保證了HolderFragment中的ViewModelStore在整個載體存續期間的惟一性和一致性,間接保證了ViewModel的惟一。
Amazing, 想明白了嗎?這麼看來onRetainInstance挺好用的,可是總以爲承載體都沒有了,真的不會內存泄漏嘛?首先官方的東西通常不會泄漏的,另外我簡單分析了一下:
1.沒有View試圖(其實有也不要緊,配置更改時View會被釋放重建的)
2.ViewModel中不持有Context對象
3.在配置更改時承載體消失只是臨時的,很快又重建並attach,若是是長時間地承載體消失,那麼它也會慢慢被垃圾回收的。

androidx轉換後

1.不變:ViewModel, AndroidViewModel, ViewModelStore, ViewModelProvider, Factory
2.變化:ViewModelStores廢棄,HolderFragment刪除。Activity/ Fragment實現了ViewModelStoreOwner類, 也就是ViewModelStore存在於Activity/ Fragment載體自身。
源碼的差別點是獲取ViewModelStore的方式如圖13。不一樣於HolderFragment統一處理,這種實現中Activity/ Fragment保存ViewModelStore的方式不相同,下面咱們分開說明。
函數

圖13.獲取ViewModelStore

FragmentActivity中ViewModelStore

關鍵點是onRetainNonConfigurationInstance(), getLastNonConfigurationInstance(), 至於實現原理自行查閱。

圖14.FragmentActivity之getViewModelStore()

若是載體真的發生配置更改,ViewModelStore則先保存到NonConfigurationInstances中,而後在OnCreate時再進行還原如圖15,圖14中再獲取一遍也沒什麼。留意下面fragments.restoreAllState則是對Fragment中ViewModelStore的還原,咱們後面再說。這裏的NonConfigurationInstances在哪裏設置進去的呢?
圖15.onCreate中還原ViewModelStore

圖16.onRetainNonConfigurationInstance

上圖16展現了NonConfigurationInstances的保存,不一樣於onSaveInstance數據格式是Bundle,這個方法容許保存對象。存儲內容分三塊用戶自定義對象,ViewModelStore對象和FragmentManagerNonConfig對象。對於用戶自定義部分看註釋發現經過重寫onRetainCustomNonConfigurationInstance()實現,ViewModelStore是咱們今天的主角,最後一個FragmentManagerNonConfig則是Fragment中數據的存儲,包含了Fragment中ViewModelStore邏輯。留意下mFragments.retainNestedNonConfig()是保存Fragment的數據,咱們後面說。
那麼ViewModelStore是怎麼在Activity.onDestroy()時銷燬呢,源碼中說只有由於配置更改致使Activity destroy時ViewModelStore是不回調clear方法的。
圖17.Activity之onDestroy

onRetainNonConfigurationInstance的調用時機是介於onStop和onDestroy之間,在onSaveInstance以後。
總結,ViewModelStore存在於FragmentActivity中,只是在配置更改致使Activity銷燬前時經過onRetainNonConfigurationInstance將數據對象保存,並在重建時經過getLastNonConfigurationInstance將數據對象還原回來,從而保證了Activity內ViewModelStore的惟一性和一致性。

Fragment中ViewModelStore

Fragment中實現保留機制大致相同,只是數據存儲在FragmentManagerNonConfig中,下圖18說明了當配置更改時Activity內須要存儲的數據,包括三部分:不被銷燬的Fragment集合(經過setRetainInstance(true)),全部子Fragment中須要保存的數據以及全部的Fragment中的ViewModelStore

圖18.FragmentManagerNonConfig類

圖19.Fragment之getViewModelStore

上圖中直接返回mViewModelStore對象,比FragmentActivity少了一步還原過程,因爲它沒有getLastNonConfigurationInstance,因此只能在FragmentActivity的onCreate中主動調用FragmentController.restoreAllState來還原Fragment相關數據(入口見圖16),下圖20簡單得展現了FragmentManager.restoreAllState方法還原ViewModelStore的過程
圖20.FragmentManager中還原ViewModelStore

Fragment中ViewModelStore的還原過程瞭解了,可是保存又是何時完成的呢? 還記得圖16中FragmentActivity的OnRetainNonConfigurationInstance()時調用FragmentController.retainNestedNonConfig()嗎,這個函數的做用就是重建Activity時重建或者還原Fragment。圖21描述了將fragment.mRetaining設置爲true的過程(注意mRetaining和mRetainInstance不同哦,前者只是在當前配置更改時不銷燬),根據註釋能夠看出mSavedNonConfig是在這個以前經過saveNonConfig設置的,其實際調用順序是 FragmentActivity.onSaveInstanceState()-> FragmentManager.saveAllState()-> FragmentManager.saveNonConfig()
圖21.FragmentManager.retainNonConfig

圖22.FragmentManager.saveNonConfig

上圖保存的數據包括三部分:經過setRetainInstance(true)須要被保存的Fragment實例,子Fragment中的保存數據以及全部Fragment中不爲空的ViewModelStore對象。它們最終會在FragmentActivity.OnRetainNonConfigurationInstance時做爲和FragmentActivity中的ViewModelStore同級別的對象一塊兒被緩存起來,直到Activity重建onCreate中被還原。
最後onDestroy時ViewModelStore.clear邏輯與FragmentActivity實現大同小異如圖23.
圖23.Fragment之onDestroy

說到這裏,Activity/ Fragment中ViewModelStore的保存和還原就介紹完了。暈乎乎的,怎麼就冒出來一個onRetainNonConfigurationInstance,其實這個方法很早就存在,能夠緩存任何你想緩存的對象,哪怕是activity實例,AnsycTask都行,這個方法用於配置更改致使Activity重建後的對象的還原。

原理總結

在android和androidx下ViewModel的存儲方式徹底不一樣,可是暴露給咱們使用的方法倒是不變的,這也說明架構的用心。當前恰逢android和androidx的交接,具體以哪一個爲準我不肯定。 上面的問題1, 5, 6的實現方式一致,在此先給出答案:
1.多個ViewModel存儲在ViewModelStore中,本質上是一個Map存儲的鍵值對。key爲ViewModel的名字或者get方法時本身設置的key。
5.先查找ViewModelStore中是否含有該實例,有則直接返回,沒有則經過反射的方式獲得實例,並加入ViewModelStore中。
6.ViewModelProviders.of(activity/fragment),經過activity/ fragment拿到Application,固然AndroidViewModel也是經過反射拿到初始實例的,和ViewModel不一樣的是它須要將application傳過去。

  • android的support包:
    2.ViewModel本質上屬於Activity/ Fragment級別,經過FragmentManager/ ChildFragmentManager插入一個沒有View的Fragment-HolderFragment,ViewModelStore即保存在HolderFragment中。
    3.在onCreate時setRetainInstance(true), 保證屏幕旋轉時fragment不被銷燬。
    4.HolderFragment在onDestroy時回調ViewModelStore.clear,循環調用viewModel.onCleared.
  • androidx包:
    2.ViewModel仍然屬於Activity/ Fragment級別,只是ViewModelStore是分別存儲在FragmentActivity和Fragment中
    3.在Activity銷燬重建過程是經過onRetainNonConfigurationInstance(), getLastNonConfigurationInstance()保存和還原的ViewModelStore對象的;
    Fragment略微複雜,在調用FragmentActivity onStop和onDestroy之間onSaveInstanceState以後saveAllState的saveNonConfig時緩存FragmentManagerNonConfig對象,裏面存儲的數據包括不被銷燬的fragment集合,循環嵌套的子Fragment的FragmentManagerNonConfig集合以及全部Fragment的ViewModelStore集合,它們在FragmentActivity重建onCreate時還原每一個Fragment的ViewModelStore對象,能夠說ViewModelStore的存儲介質是正常被銷燬的,只是ViewModelStore數據被保存並在特定場景下還原回來,保存和還原都是由FragmentActivity主動觸發的;
    3.若是是由配置更改致使Activity/ Fragment onDestroy則不清除ViewModelStore的數據,反之則清除。
    總之,無論採用不銷燬ViewModelStore的存儲介質,仍是採用保存再還原數據的方式,都能保證在當前做用域下數據的惟一性和完整性。代碼提及來複雜,可是設計思想值得咱們去消化學習。

關注微信公衆號,最新技術乾貨實時推送

image
相關文章
相關標籤/搜索