Android?ViewModel創(chuàng)建不受橫豎屏切換影響原理詳解
ViewModel的創(chuàng)建方式
在我們項目中, 引入了viewModel 做MVI 設(shè)計模式的組成部分,它是JetPack 組件庫中的重要成員。今天來了解下它。
// 在 Activity 中使用 class MainActivity : AppCompatActivity() { // 使用 Activity 的作用域 private val viewModel : MainViewModel by viewModels() } // 在 Fragment 中使用 class MainFragment : Fragment() { // 使用 Activity 的作用域,與 MainActivity 使用同一個對象 val activityViewModel : MainViewModel by activityViewModels() // 使用 Fragment 的作用域 val viewModel : MainViewModel by viewModels() }
// ViewModel 創(chuàng)建工廠 private final Factory mFactory; // ViewModel 存儲容器 private final ViewModelStore mViewModelStore; // 默認(rèn)使用 NewInstanceFactory 反射創(chuàng)建 ViewModel public ViewModelProvider(ViewModelStoreOwner owner) { this(owner.getViewModelStore(), ... NewInstanceFactory.getInstance()); } // 自定義 ViewModel 創(chuàng)建工廠 public ViewModelProvider(ViewModelStoreOwner owner, Factory factory) { this(owner.getViewModelStore(), factory); } // 記錄宿主的 ViewModelStore 和 ViewModel 工廠 public ViewModelProvider(ViewModelStore store, Factory factory) { mFactory = factory; mViewModelStore = store; } @NonNull @MainThread public <T extends ViewModel> T get(Class<T> modelClass) { String canonicalName = modelClass.getCanonicalName(); if (canonicalName == null) { throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels"); } // 使用類名作為緩存的 KEY return get(DEFAULT_KEY + ":" + canonicalName, modelClass); } // Fragment @NonNull @MainThread public <T extends ViewModel> T get(String key, Class<T> modelClass) { // 1. 先從 ViewModelStore 中取緩存 ViewModel viewModel = mViewModelStore.get(key); if (modelClass.isInstance(viewModel)) { return (T) viewModel; } // 2. 使用 ViewModel 工廠創(chuàng)建實例 viewModel = mFactory.create(modelClass); ... // 3. 存儲到 ViewModelStore mViewModelStore.put(key, viewModel); return (T) viewModel; } // 默認(rèn)的 ViewModel 工廠 public static class NewInstanceFactory implements Factory { private static NewInstanceFactory sInstance; @NonNull static NewInstanceFactory getInstance() { if (sInstance == null) { sInstance = new NewInstanceFactory(); } return sInstance; } @NonNull @Override public <T extends ViewModel> T create(Class<T> modelClass) { // 反射創(chuàng)建 ViewModel 對象 return modelClass.newInstance(); } }
可以看到:
ViewModel 實例的方法最終是通過 ViewModelProvider 完成的。ViewModelProvider 可以理解為創(chuàng)建 ViewModel 的工具類,它需要 2 個參數(shù):
參數(shù) 1 ViewModelStoreOwner:
它對應(yīng)于 Activity / Fragment 等持有 ViewModel 的宿主,它們內(nèi)部通過 ViewModelStore 維持一個 ViewModel 的映射表,ViewModelStore 是實現(xiàn) ViewModel 作用域和數(shù)據(jù)恢復(fù)的關(guān)鍵;
參數(shù) 2 Factory:
它對應(yīng)于 ViewModel 的創(chuàng)建工廠,缺省時將使用默認(rèn)的 NewInstanceFactory 工廠來反射創(chuàng)建 ViewModel 實例。
創(chuàng)建 ViewModelProvider 工具類后,你將通過 get() 方法來創(chuàng)建 ViewModel 的實例。get() 方法內(nèi)部首先會通過 ViewModel 的全限定類名從映射表(ViewModelStore)中取緩存,未命中才會通過 ViewModel 工廠創(chuàng)建實例再緩存到映射表中。 正因為同一個 ViewModel 宿主使用的是同一個 ViewModelStore 映射表,因此在同一個宿主上重復(fù)調(diào)用 ViewModelProvider.get() 返回同一個 ViewModel 實例。
// ViewModel 本質(zhì)上就是一個映射表而已 public class ViewModelStore { // <String - ViewModel> 哈希表 private final HashMap<String, ViewModel> mMap = new HashMap<>(); final void put(String key, ViewModel viewModel) { ViewModel oldViewModel = mMap.put(key, viewModel); if (oldViewModel != null) { oldViewModel.onCleared(); } } final ViewModel get(String key) { return mMap.get(key); } Set<String> keys() { return new HashSet<>(mMap.keySet()); } public final void clear() { for (ViewModel vm : mMap.values()) { vm.clear(); } mMap.clear(); } }
ViewModel 宿主是 ViewModelStoreOwner 接口的實現(xiàn)類,例如 ComponentActivity:
public class ComponentActivity extends androidx.core.app.ComponentActivity implements ContextAware, LifecycleOwner, ViewModelStoreOwner ... { // ViewModel 的存儲容器 private ViewModelStore mViewModelStore; // ViewModel 的創(chuàng)建工廠 private ViewModelProvider.Factory mDefaultFactory; @NonNull @Override public ViewModelStore getViewModelStore() { if (mViewModelStore == null) { // 已簡化過程 mViewModelStore = new ViewModelStore(); } return mViewModelStore; } }
由此,ViewModel 的創(chuàng)建其實跟activity 是相關(guān)聯(lián)的。
ViewModel 為什么不受 Activity 橫豎屏生命周期的影響
1、在 Activity 走到 onDestroy 方法時,做了判斷 isChangingConfigurations
可以看到,在 ComponentActivity
構(gòu)造方法中添加了生命周期的判斷,當(dāng) Activity onDestroy 時,如果是發(fā)生了橫豎屏切換,就不會走 getViewModelStore().clear()
,清理操作,保證了ViewModel 持有的數(shù)據(jù)還能存在。
2、在 Activity 獲取 getViewModelStore 時,
通過getLastNonConfigurationInstance()
獲取 NonConfigurationInstances
實例,從而得到這個實例中的 viewModelStore
而且,Activity 生命周期的變化都會走這個方法,來保證viewModelStore
不為空。
3、onRetainNonConfigurationInstance 調(diào)用
在 Activity 屏幕旋轉(zhuǎn)時,onRetainNonConfigurationInstance()
在onStop
和onDestroy
之間調(diào)用
onRetainNonConfigurationInstance()
是個重要的方法
這保證了 在銷毀前,viewModelStore
實例被拿到并交給 NonConfigurationInstances
實例,將 viewModelStore
賦值給他
總結(jié)
1、介紹了ViewModel的創(chuàng)建
- ViewModel 由創(chuàng)建工廠 Factory mFactory 反射創(chuàng)建而來,并被放到存儲容器 ViewModelStore 中
2、ViewModel生命周期和橫豎屏場景下不被銷毀的原因
在 Activity 走到 onDestroy 方法時,做了判斷 isChangingConfigurations
,如果發(fā)生橫豎屏,不會清理ViewModelStore,所以ViewModel還在
銷毀前 onRetainNonConfigurationInstance()
被觸發(fā),將原來的viewModelStore
賦值給 NonConfigurationInstances
實例。
從Activity重建后 調(diào)用 getViewModelStore
方法,內(nèi)部 通過 getLastNonConfigurationInstance()
獲取 NonConfigurationInstances
, NonConfigurationInstances
中保存的viewModelStore
實例被拿到,而viewModelStore
中保存著 ViewModel 。
以上就是Android ViewModel創(chuàng)建不受橫豎屏切換影響原理詳解的詳細(xì)內(nèi)容,更多關(guān)于Android ViewModel創(chuàng)建的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android保存的文件顯示到文件管理的最近文件和下載列表中的方法
這篇記錄的是Android中如何把我們往存儲中寫入的文件,如何顯示到文件管理的下載列表、最近文件列表中,需要的朋友可以參考下2020-01-01Android基于Intent實現(xiàn)Activity之間數(shù)據(jù)傳遞的方法
這篇文章主要介紹了Android基于Intent實現(xiàn)Activity之間數(shù)據(jù)傳遞的方法,結(jié)合實例形式分析了Activity之間數(shù)據(jù)傳遞操作的相關(guān)技巧,代碼備有較為詳盡的注釋,需要的朋友可以參考下2016-11-11Android之有效防止按鈕多次重復(fù)點擊的方法(必看篇)
下面小編就為大家?guī)硪黄狝ndroid之有效防止按鈕多次重復(fù)點擊的方法(必看篇)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-04-04android為ListView每個Item上面的按鈕添加事件
本篇文章主要介紹了android為ListView每個Item上面的按鈕添加事件,有興趣的同學(xué)可以了解一下。2016-11-11Android編程實現(xiàn)橫豎屏切換時不銷毀當(dāng)前activity和鎖定屏幕的方法
這篇文章主要介紹了Android編程實現(xiàn)橫豎屏切換時不銷毀當(dāng)前activity和鎖定屏幕的方法,涉及Android屬性設(shè)置及activity操作的相關(guān)技巧,需要的朋友可以參考下2015-11-11Kotlin基礎(chǔ)學(xué)習(xí)之循環(huán)和異常
最近在學(xué)習(xí)kotlin,Kotlin 是一個基于 JVM 的新的編程語言,下面這篇文章主要給大家介紹了關(guān)于Kotlin基礎(chǔ)學(xué)習(xí)之循環(huán)和異常的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧。2017-12-12