Android ViewModel的作用深入講解
ViewModel它的作用是什么呢
ViewModel 類旨在以注重生命周期的方式存儲和管理界面相關(guān)數(shù)據(jù)。ViewModel 類讓數(shù)據(jù)可在發(fā)生屏幕旋轉(zhuǎn)等配置更改后繼續(xù)留存(官方解釋)
看到這里我們就可以總結(jié)viewmodel的兩個作用點,第一viewmodel在activity和fragment銷毀時自己也會被清除掉,第二點viewmodel在屏幕旋轉(zhuǎn)activity銷毀后重建可以顯示之前數(shù)據(jù)。
那么問題就來了viewmodel是怎么保存數(shù)據(jù)的以及自動釋放掉內(nèi)存? 這兩個問題弄懂了viewmodel的面紗也就被我們揭開了。
那我們就直接從最簡單的使用viewmodel開始說起
class UserViewModel : ViewModel() { var age: Int = 0 } class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val userViewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())[UserViewModel::class.java] val mView = ActivityMainBinding.inflate(layoutInflater) setContentView(mView.root) mView.tv.text = userViewModel.age.toString() var sum = 0 mView.tv.setOnClickListener { sum = sum.inc() userViewModel.age = sum } } }
隨著我們不停的點擊 sum會越來越大 當(dāng)我們旋轉(zhuǎn)屏幕的時候 activity會重建 但是我們獲取的age卻是最后一次點擊的值,這就證明了我們數(shù)據(jù)是被保存了下來。那么viewmodel是怎么做到的呢?我們從源碼角度去分析
以下源碼分析是從Android13分析的
看源碼viewmodel是一個抽象類,并不能看出什么。那么我們就得換一個思路去思考了。既然viewmodel是和activity有關(guān)系,而且在activity旋轉(zhuǎn)銷毀時還能做到復(fù)用,那么我們就從activity中去尋找。
一級一級尋找發(fā)現(xiàn)在ComponentActivity實現(xiàn)了一個ViewModelStoreOwner接口 看命名是和viewmodel有點關(guān)系看下這個接口內(nèi)部有什么
public interface ViewModelStoreOwner { /** * Returns owned {@link ViewModelStore} * * @return a {@code ViewModelStore} */ @NonNull ViewModelStore getViewModelStore(); } //代碼很簡潔 一個抽象方法 返回值ViewModelStore 顧名思義這個類的功能也就呼之欲出,存儲viewmodel,那我們就看實現(xiàn)類中怎么處理的 //ComponentActivity中實現(xiàn) @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; } //我們直接看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(); } } } //ensureViewModelStore方法看來是為了獲取ViewModelStore,那我們在具體看下ViewModelStore內(nèi)部做了什么? public class ViewModelStore { 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()); } /** * Clears internal storage and notifies ViewModels that they are no longer used. */ public final void clear() { for (ViewModel vm : mMap.values()) { vm.clear(); } mMap.clear(); } } //看到這里就明了了,果然和我們猜想的一樣,ViewModelStore是用來緩存ViewModel的
經(jīng)過我們分析已經(jīng)明白了viewmodel是被ViewModelStore緩存起來的,那么又是如何做到在activity不正常銷毀時去恢復(fù)數(shù)據(jù)的呢?
在ComponentActivity在發(fā)現(xiàn)還有另一個方法中使用了ViewModelStore
onRetainNonConfigurationInstance方法
public final Object onRetainNonConfigurationInstance() { // Maintain backward compatibility. Object custom = onRetainCustomNonConfigurationInstance(); ViewModelStore viewModelStore = mViewModelStore; if (viewModelStore == null) { // No one called getViewModelStore(), so see if there was an existing // ViewModelStore from our last NonConfigurationInstance NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { viewModelStore = nc.viewModelStore; } } if (viewModelStore == null && custom == null) { return null; } NonConfigurationInstances nci = new NonConfigurationInstances(); nci.custom = custom; nci.viewModelStore = viewModelStore; return nci; }
方法體內(nèi)的代碼也很容易理解 如果viewModelStore為null 就去給它賦值。那么這個方法是在什么時候執(zhí)行的呢?經(jīng)過一番debug發(fā)現(xiàn)在activity切換橫豎屏的時候 這個方法就被觸發(fā)了 而getViewModelStore方法在activity創(chuàng)建的時候就執(zhí)行了。我們現(xiàn)在知道了viewModelStore的創(chuàng)建時機,那么viewmodel是如何存儲到viewModelStore中的呢?
還記得我們寫的示例代碼嗎?
val userViewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()) .get(UserViewModel::class.java)
我們就從ViewModelProvider入手
public constructor(owner: ViewModelStoreOwner, factory: Factory) : this( owner.viewModelStore, factory, defaultCreationExtras(owner) )
第一個入?yún)⒕褪俏覀僡ctivity實例 然后拿到我們自己的viewModelStore,這個時候的viewModelStore已經(jīng)創(chuàng)建好了,看第二個參數(shù)是Factory 我們傳遞的是NewInstanceFactory這個一看就是單例,內(nèi)部實現(xiàn)了一個create方法
public open class NewInstanceFactory : Factory { @Suppress("DocumentExceptions") override fun <T : ViewModel> create(modelClass: Class<T>): T { return try { modelClass.newInstance() } catch (e: InstantiationException) { throw RuntimeException("Cannot create an instance of $modelClass", e) } catch (e: IllegalAccessException) { throw RuntimeException("Cannot create an instance of $modelClass", e) } }
一個泛型方法返回一個自定義的viewmodel實例,但是還是沒看到如何存儲的viewmodel,別急
我們再來看最后調(diào)用的get方法
@MainThread public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T { val viewModel = store[key] if (modelClass.isInstance(viewModel)) { (factory as? OnRequeryFactory)?.onRequery(viewModel) return viewModel as T } else { @Suppress("ControlFlowWithEmptyBody") if (viewModel != null) { // TODO: log a warning. } } val extras = MutableCreationExtras(defaultCreationExtras) extras[VIEW_MODEL_KEY] = key // AGP has some desugaring issues associated with compileOnly dependencies so we need to // fall back to the other create method to keep from crashing. return try { factory.create(modelClass, extras) } catch (e: AbstractMethodError) { factory.create(modelClass) }.also { store.put(key, it) } }
首選會從ViewModelStore中獲取viewmodel ,看if語句內(nèi)部就可以看出直接返回的是緩存的viewmodel,如果不存在則根據(jù)創(chuàng)建的factory去實例化viewmodel然后并存儲到ViewModelStore中。
經(jīng)過我們的源碼分析,我們現(xiàn)在已經(jīng)明白了viewmodel的存儲過程和如何在activity銷毀時獲取的流程。
那么viewmodel又是如何銷毀的呢?還記得viewmodel中的onCleared方法嗎?注釋就寫明了當(dāng)這個ViewModel不再使用并被銷毀時,這個方法將被調(diào)用。 那么就來看這個方法在什么時候調(diào)用的
內(nèi)部有一個clear該方法又被ViewModelStore的clear方法調(diào)用,接著又被ComponentActivity內(nèi)部
getLifecycle().addObserver(new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { if (event == Lifecycle.Event.ON_DESTROY) { // Clear out the available context mContextAwareHelper.clearAvailableContext(); // And clear the ViewModelStore if (!isChangingConfigurations()) { getViewModelStore().clear(); } } } });
用到了Lifecycle去監(jiān)聽生命周期當(dāng)activity不正常銷毀時,則清除掉緩存的viewmodel。至此我們就搞懂了viewmodel是如何實現(xiàn)了對數(shù)據(jù)的存儲和以及數(shù)據(jù)的獲取。
這里還有一點需要額外說明,ViewModelStore也是從緩存中取得, 在getViewModelStore方法和onRetainNonConfigurationInstance方法中 都能看到getLastNonConfigurationInstance方法的身影。不為null,就獲取緩存的ViewModelStore,那就自然能獲取到之前存儲的viewModel 至于怎么緩存的各位大佬自己研究吧!
至此 ,我們已經(jīng)搞懂了viewmodel是如何做到在activity銷毀時自動清除和銷毀重建顯示之前數(shù)據(jù)。
到此這篇關(guān)于Android ViewModel的作用深入講解的文章就介紹到這了,更多相關(guān)Android ViewModel內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android之利用EventBus發(fā)送消息傳遞示例
本篇文章主要介紹了Android之利用EventBus進行消息傳遞示例。EventBus是一款針對Android優(yōu)化的發(fā)布/訂閱事件總線,非常具有實用價值,需要的朋友可以參考下。2017-02-02Android開發(fā)使用自定義View將圓角矩形繪制在Canvas上的方法
這篇文章主要介紹了Android開發(fā)使用自定義View將圓角矩形繪制在Canvas上的方法,結(jié)合實例形式分析了Android自定義view繪制圓角矩形的相關(guān)方法與使用技巧,需要的朋友可以參考下2017-10-10詳解Android開發(fā)中Activity的四種launchMode
這篇文章主要介紹了Android開發(fā)中Activity的四種launchMode,launchMode主要用于控制多個Activity間的跳轉(zhuǎn),需要的朋友可以參考下2016-03-03Android MPAndroidChart開源庫圖表之折線圖的實例代碼
這篇文章主要介紹了Android MPAndroidChart開源庫圖表之折線圖的實例代碼,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05