前言
- 為了更好了解ViewModel谜悟,建議看下幾篇文章
知識(shí)準(zhǔn)備
- 重要知識(shí)介紹(后面用到)
這個(gè)方法是fragment的方法司忱,根據(jù)官方注釋,我們知道一旦我們設(shè)置setRetainInstance(true),意味著當(dāng)我們旋轉(zhuǎn)屏幕的時(shí)候锄蹂,activity重繪氓仲,fragment不會(huì)重繪,它將會(huì)保留。也就意味著Fragment的onCreate和onDestory方法都不會(huì)調(diào)用敬扛,這樣的特性很多被用來用狀態(tài)保存,數(shù)據(jù)恢復(fù)晰洒。具體是怎么樣的過程,請看fragment源碼分析/** * Control whether a fragment instance is retained across Activity * re-creation (such as from a configuration change). This can only * be used with fragments not in the back stack. If set, the fragment * lifecycle will be slightly different when an activity is recreated: * <ul> * <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still * will be, because the fragment is being detached from its current activity). * <li> {@link #onCreate(Bundle)} will not be called since the fragment * is not being re-created. * <li> {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} <b>will</b> * still be called. * </ul> */ public void setRetainInstance(boolean retain) { mRetainInstance = retain; }
ViewModel是什么啥箭?
-
定義
ViewModel類旨在以一種有生命周期的方式存儲(chǔ)和管理與UI相關(guān)的數(shù)據(jù)谍珊。 ViewModel類允許數(shù)據(jù)在屏幕旋轉(zhuǎn)等配置變化后存活
-
解決的問題
Android框架控制著UI控制器(Activity或者Fragment)的生命周期,它就有可能決定銷毀或者重建我們的UI控制急侥,以響應(yīng)完全不受控制的某些用戶操作或設(shè)備事件砌滞。那么就會(huì)出現(xiàn)幾個(gè)問題。
-
如果被銷毀那么存儲(chǔ)在其中的所有瞬態(tài)UI相關(guān)數(shù)據(jù)都將丟失.
例如:您的應(yīng)用可能會(huì)在其中一個(gè)活動(dòng)中包含用戶列表坏怪。 當(dāng)針對(duì)配置更改重新創(chuàng)建活動(dòng)時(shí)贝润,新活動(dòng)必須重新獲取用戶列表。 對(duì)于簡單的數(shù)據(jù)陕悬,活動(dòng)可以使用onSaveInstanceState()方法并從onCreate()的包中恢復(fù)其數(shù)據(jù)题暖,但是這種方法僅適用于可以序列化然后反序列化的少量數(shù)據(jù),而不適用于潛在的大量數(shù)據(jù)像用戶或位圖的列表捉超。
-
UI控制獲取數(shù)據(jù)的時(shí)候經(jīng)常進(jìn)行異步調(diào)用,可能需要一些時(shí)間來返回唯绍。 UI控制器需要管理這些調(diào)用拼岳,并確保系統(tǒng)在銷毀后清理它們以避免潛在的內(nèi)存泄漏。
這種管理需要大量的維護(hù)况芒,并且在為配置更改而重新創(chuàng)建對(duì)象的情況下惜纸,由于對(duì)象可能不得不重新發(fā)出已經(jīng)做出的調(diào)用(可能會(huì)重新請求數(shù)據(jù)),所以浪費(fèi)資源.
-
UI控制器(如活動(dòng)和片段)主要用于顯示UI數(shù)據(jù)绝骚,對(duì)用戶操作做出反應(yīng)耐版,或處理操作系統(tǒng)通信(如權(quán)限請求)。
如果要求UI控制器也負(fù)責(zé)從數(shù)據(jù)庫或網(wǎng)絡(luò)加載數(shù)據(jù)压汪,從而增加了該類的膨脹粪牲。 為UI控制器分配過多的責(zé)任可能會(huì)導(dǎo)致一個(gè)類嘗試單獨(dú)處理應(yīng)用程序的所有工作,而不是將工作委托給其他類止剖。 通過這種方式給UI控制器分配過多的責(zé)任也使測試變得更加困難腺阳。
-
用法簡介
-
依賴
compile "android.arch.lifecycle:runtime:1.0.3" compile "android.arch.lifecycle:extensions:1.0.0-rc1" annotationProcessor "android.arch.lifecycle:compiler:1.0.0-rc1"
-
api用法
- 創(chuàng)建ViewModel
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 asyncronous operation to fetch users. } }
ViewModel對(duì)象在配置更改期間自動(dòng)保留,以便它們保存的數(shù)據(jù)立即可用于下一個(gè)activity或fragment
- acitivity或fragment獲取數(shù)據(jù)
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 }); } }
如果Activity重新繪制穿香,但是ViewModel實(shí)例并不會(huì)被銷毀亭引,而是重新從ViewModel中獲取數(shù)據(jù),等到Activity銷毀(退出或者被系統(tǒng)殺死)皮获,框架會(huì)調(diào)用onCleared()方法焙蚓,以便清理資源;
警告:viewModel絕不能引用視圖, Lifecycle或任何可能持有對(duì)活動(dòng)上下文的引用的類购公。
ViewModel可以包含lifeCycleObservers.例如liveData赵哲。但是ViewModel絕不能觀察生命周期感知的可觀察對(duì)象(LiveData對(duì)象的更改),如果ViewModel需要上下文引用,請用AndroidModelView,它含有Application的對(duì)象.
ViewModel的生命周期
從圖中可以看出我們發(fā)現(xiàn)君丁,當(dāng)activity因屏幕旋轉(zhuǎn)而銷毀枫夺,但是ViewModel一直存在,也就是這個(gè)對(duì)象一直都在(框架核心绘闷,怎么實(shí)現(xiàn)源碼分析會(huì)講到)橡庞,直到finished才調(diào)用clear清除內(nèi)存。
Fragment之間共享數(shù)據(jù)
-
一個(gè)Activity中兩個(gè)Fragment進(jìn)行通信是很常見的印蔗,想象一下扒最,主 - 從Fragmetn的一種常見情況,其中有一個(gè)片段华嘹,用戶從列表中選擇一個(gè)項(xiàng)目吧趣,另一個(gè)片段顯示所選項(xiàng)目的內(nèi)容。這種情況從來都不是微不足道的耙厚,因?yàn)檫@兩個(gè)片段都需要定義一些接口描述强挫,而所有者活動(dòng)必須將兩者聯(lián)系在一起。 此外薛躬,這兩個(gè)片段必須處理其他片段尚未創(chuàng)建或可見的場景俯渤。
對(duì)于這個(gè)常見的痛點(diǎn),可以用ViewModel來解決型宝,這些Fragment可以使用Activity的Scope來共享一個(gè)ViewModel八匠,來處理通信。
public class SharedViewModel extends ViewModel { private final MutableLiveData<Item> selected = new MutableLiveData<Item>(); public void select(Item item) { selected.setValue(item); } public LiveData<Item> getSelected() { return selected; } } public class MasterFragment extends Fragment { private SharedViewModel model; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class); itemSelector.setOnClickListener(item -> { model.select(item); }); } } public class DetailFragment extends Fragment { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class); model.getSelected().observe(this, { item -> // Update the UI. }); } }
注意:我們在獲取ViewModelProvider時(shí)趴酣,這兩個(gè)是使用的是getActivity()梨树,而不是當(dāng)前的Fragment(后面會(huì)分析源碼)。所以兩個(gè)Fragment用的是同一個(gè)對(duì)象岖寞。
好處如下:
- 對(duì)于Activity來說抡四,他不知道這個(gè)交流,也不用管理
- 對(duì)于Framgent慎璧,除了共同擁有這個(gè)SharedViewModel床嫌,他們之間不需要了解彼此。如果一個(gè)Framgnet消失不會(huì)影響另一個(gè)Framgent.
- 每個(gè)Framgent都有自己獨(dú)立的生命周期胸私,不互相影響厌处。
源碼分析
- 我們先找到程序的入口
ViewModelProviders.of(this).get(MyViewModel.class)
-
先看of方法
return new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory)
返回一個(gè)ViewModelProvider,需要兩個(gè)參數(shù)ViewModelStore(存放ViewModel的倉庫)岁疼,F(xiàn)actory(用工廠模式創(chuàng)建我們的ViewModel對(duì)象)阔涉。假如說我們的Activity重新繪制了缆娃,那么這里的ViewModelProvider就是不同的實(shí)例。這個(gè)類主要的作用就是獲取我們ViewModel對(duì)象瑰排。
-
看一下of()方法
ViewModelStores.of(activity)
ViewModelStores是干什么的呢贯要?提供一些靜態(tài)方法獲取所傳入activity的ViewModeStore.
@MainThread public static ViewModelStore of(@NonNull FragmentActivity activity) { return holderFragmentFor(activity).getViewModelStore(); }
-
holderFragmentFor
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public static HolderFragment holderFragmentFor(FragmentActivity activity) { return sHolderFragmentManager.holderFragmentFor(activity); }
- 我們看到是靜態(tài)方法,這個(gè)方法不是ViewModelStore椭住,而是
HolderFragment的一個(gè)靜態(tài)方法崇渗,返回一個(gè)HolderFragment對(duì)象. - 然后回到3處調(diào)用getViewModelStore,返回ViewModelStore京郑。所以說HolderFragment中有一個(gè)ViewModelStore宅广。對(duì)應(yīng)關(guān)系是一個(gè)HolderFrament含有一個(gè)ViewModelStore(存放ViewModel的倉庫),一個(gè)ViewModelStore存放著多個(gè)ViewModel。
- HolderFragmentManager是屬于HolderFramgent的靜態(tài)內(nèi)部類
- 我們看到是靜態(tài)方法,這個(gè)方法不是ViewModelStore椭住,而是
-
在(iii)處調(diào)用了sHolderFragmentManager.holderFragmentFor
HolderFragment holderFragmentFor(FragmentActivity activity) { FragmentManager fm = activity.getSupportFragmentManager(); HolderFragment holder = findHolderFragment(fm); if (holder != null) { return holder; } holder = mNotCommittedActivityHolders.get(activity); if (holder != null) { return holder; } holder = createHolderFragment(fm); mNotCommittedActivityHolders.put(activity, holder); return holder; }
-
首先在在Activity的supportFragmentManager中的查找些举,有的話就會(huì)返回
private static HolderFragment findHolderFragment(FragmentManager manager) { if (manager.isDestroyed()) { throw new IllegalStateException("Can't access ViewModels from onDestroy"); } Fragment fragmentByTag = manager.findFragmentByTag(HOLDER_TAG); if (fragmentByTag != null && !(fragmentByTag instanceof HolderFragment)) { throw new IllegalStateException("Unexpected " + "fragment instance was returned by HOLDER_TAG"); } return (HolderFragment) fragmentByTag; }
-
沒有的話在我們存放的集合中查找有的話就返回,在HolderFragmentManager集合如下
private Map<Activity, HolderFragment> mNotCommittedActivityHolders = new HashMap<>();
可以看出多個(gè)Activity可能對(duì)應(yīng)一個(gè)HolderFrament
-
沒有的話就創(chuàng)建并放入我們的mNotCommittedActivityHolders跟狱。
private static HolderFragment createHolderFragment(FragmentManager fragmentManager) { HolderFragment holder = new HolderFragment(); fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss(); return holder; }
創(chuàng)建Fragment對(duì)象,這是一個(gè)無ui的Fragment户魏,==主要作用就是用ViewModelStore儲(chǔ)存我們的ViewModel驶臊,并在Fragement的OnDestory調(diào)用ViewModelStore的clear釋放所有內(nèi)存==。
有人要說叼丑,fragment對(duì)象還是要重建啊关翎,那么ViewModel也要重建啊,因?yàn)樗鼘儆贔ragment幢码,這就用到了文章開篇講到了setRetainInstance方法笤休。看下源碼
public HolderFragment() { setRetainInstance(true); }
在我們初始化的時(shí)候會(huì)調(diào)用setRetainInstance症副,這就解決這個(gè)問題了。
-
拿到HolderFragment對(duì)象政基,回到(ii)處調(diào)用getViewModelStore贞铣,拿到ViewModelStore對(duì)象。
-
- 我們拿到ViweModelStore回到(1)處沮明,我們就去拿我們的ViewModel辕坝。看源碼
@NonNull public <T extends ViewModel> T get(@NonNull Class<T> modelClass) { String canonicalName = modelClass.getCanonicalName(); if (canonicalName == null) { throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels"); } return get(DEFAULT_KEY + ":" + canonicalName, modelClass); }
- 看get(DEFAULT_KEY + ":" + canonicalName, modelClass)
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) { ViewModel viewModel = mViewModelStore.get(key); if (modelClass.isInstance(viewModel)) { //noinspection unchecked return (T) viewModel; } else { //noinspection StatementWithEmptyBody if (viewModel != null) { // TODO: log a warning. } } viewModel = mFactory.create(modelClass); mViewModelStore.put(key, viewModel); //noinspection unchecked return (T) viewModel; }
- 首先是我們從mViewModelStore取出我們想要的ViewModel.
有的話就返回 - 沒有的話就利用工廠模式反射生產(chǎn)我們所要的ViewModel對(duì)象荐健,同時(shí)把我們的ViewModel對(duì)象放入mViewModelStore酱畅。同時(shí)返回我們的ViewModel.
總結(jié):
核心思想就是HolderFragment調(diào)用setsetRetainInstance(true),使得HolderFragment在FragmentMannager調(diào)用FindFragmentBytag,找到的是同一個(gè)HolderFragment對(duì)象(無論Activity是否重繪),這也保證了HolderFragment中的ViewModelStore(存放我們ViewModel的地方)不被銷毀江场,然后我們?nèi)〕鑫覀兯腣iewModel,進(jìn)行數(shù)據(jù)讀取纺酸。