Android ViewModel的作用深入講解
ViewModel它的作用是什么呢
ViewModel 類旨在以注重生命周期的方式存儲(chǔ)和管理界面相關(guān)數(shù)據(jù)。ViewModel 類讓數(shù)據(jù)可在發(fā)生屏幕旋轉(zhuǎn)等配置更改后繼續(xù)留存(官方解釋)
看到這里我們就可以總結(jié)viewmodel的兩個(gè)作用點(diǎn),第一viewmodel在activity和fragment銷毀時(shí)自己也會(huì)被清除掉,第二點(diǎn)viewmodel在屏幕旋轉(zhuǎn)activity銷毀后重建可以顯示之前數(shù)據(jù)。
那么問題就來了viewmodel是怎么保存數(shù)據(jù)的以及自動(dòng)釋放掉內(nèi)存? 這兩個(gè)問題弄懂了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 } } }
隨著我們不停的點(diǎn)擊 sum會(huì)越來越大 當(dāng)我們旋轉(zhuǎn)屏幕的時(shí)候 activity會(huì)重建 但是我們獲取的age卻是最后一次點(diǎn)擊的值,這就證明了我們數(shù)據(jù)是被保存了下來。那么viewmodel是怎么做到的呢?我們從源碼角度去分析
以下源碼分析是從Android13分析的
看源碼viewmodel是一個(gè)抽象類,并不能看出什么。那么我們就得換一個(gè)思路去思考了。既然viewmodel是和activity有關(guān)系,而且在activity旋轉(zhuǎn)銷毀時(shí)還能做到復(fù)用,那么我們就從activity中去尋找。
一級(jí)一級(jí)尋找發(fā)現(xiàn)在ComponentActivity實(shí)現(xiàn)了一個(gè)ViewModelStoreOwner接口 看命名是和viewmodel有點(diǎn)關(guān)系看下這個(gè)接口內(nèi)部有什么
public interface ViewModelStoreOwner { /** * Returns owned {@link ViewModelStore} * * @return a {@code ViewModelStore} */ @NonNull ViewModelStore getViewModelStore(); } //代碼很簡潔 一個(gè)抽象方法 返回值ViewModelStore 顧名思義這個(gè)類的功能也就呼之欲出,存儲(chǔ)viewmodel,那我們就看實(shí)現(xiàn)類中怎么處理的 //ComponentActivity中實(shí)現(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,那我們?cè)诰唧w看下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不正常銷毀時(shí)去恢復(fù)數(shù)據(jù)的呢?
在ComponentActivity在發(fā)現(xiàn)還有另一個(gè)方法中使用了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 就去給它賦值。那么這個(gè)方法是在什么時(shí)候執(zhí)行的呢?經(jīng)過一番debug發(fā)現(xiàn)在activity切換橫豎屏的時(shí)候 這個(gè)方法就被觸發(fā)了 而getViewModelStore方法在activity創(chuàng)建的時(shí)候就執(zhí)行了。我們現(xiàn)在知道了viewModelStore的創(chuàng)建時(shí)機(jī),那么viewmodel是如何存儲(chǔ)到viewModelStore中的呢?
還記得我們寫的示例代碼嗎?
val userViewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()) .get(UserViewModel::class.java)
我們就從ViewModelProvider入手
public constructor(owner: ViewModelStoreOwner, factory: Factory) : this( owner.viewModelStore, factory, defaultCreationExtras(owner) )
第一個(gè)入?yún)⒕褪俏覀僡ctivity實(shí)例 然后拿到我們自己的viewModelStore,這個(gè)時(shí)候的viewModelStore已經(jīng)創(chuàng)建好了,看第二個(gè)參數(shù)是Factory 我們傳遞的是NewInstanceFactory這個(gè)一看就是單例,內(nèi)部實(shí)現(xiàn)了一個(gè)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) } }
一個(gè)泛型方法返回一個(gè)自定義的viewmodel實(shí)例,但是還是沒看到如何存儲(chǔ)的viewmodel,別急
我們?cè)賮砜醋詈笳{(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) } }
首選會(huì)從ViewModelStore中獲取viewmodel ,看if語句內(nèi)部就可以看出直接返回的是緩存的viewmodel,如果不存在則根據(jù)創(chuàng)建的factory去實(shí)例化viewmodel然后并存儲(chǔ)到ViewModelStore中。
經(jīng)過我們的源碼分析,我們現(xiàn)在已經(jīng)明白了viewmodel的存儲(chǔ)過程和如何在activity銷毀時(shí)獲取的流程。
那么viewmodel又是如何銷毀的呢?還記得viewmodel中的onCleared方法嗎?注釋就寫明了當(dāng)這個(gè)ViewModel不再使用并被銷毀時(shí),這個(gè)方法將被調(diào)用。 那么就來看這個(gè)方法在什么時(shí)候調(diào)用的
內(nèi)部有一個(gè)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不正常銷毀時(shí),則清除掉緩存的viewmodel。至此我們就搞懂了viewmodel是如何實(shí)現(xiàn)了對(duì)數(shù)據(jù)的存儲(chǔ)和以及數(shù)據(jù)的獲取。
這里還有一點(diǎn)需要額外說明,ViewModelStore也是從緩存中取得, 在getViewModelStore方法和onRetainNonConfigurationInstance方法中 都能看到getLastNonConfigurationInstance方法的身影。不為null,就獲取緩存的ViewModelStore,那就自然能獲取到之前存儲(chǔ)的viewModel 至于怎么緩存的各位大佬自己研究吧!
至此 ,我們已經(jīng)搞懂了viewmodel是如何做到在activity銷毀時(shí)自動(dòng)清除和銷毀重建顯示之前數(shù)據(jù)。
到此這篇關(guān)于Android ViewModel的作用深入講解的文章就介紹到這了,更多相關(guān)Android ViewModel內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android之利用EventBus發(fā)送消息傳遞示例
本篇文章主要介紹了Android之利用EventBus進(jìn)行消息傳遞示例。EventBus是一款針對(duì)Android優(yōu)化的發(fā)布/訂閱事件總線,非常具有實(shí)用價(jià)值,需要的朋友可以參考下。2017-02-02Android實(shí)現(xiàn)多線程斷點(diǎn)續(xù)傳
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)多線程斷點(diǎn)續(xù)傳,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07android多線程斷點(diǎn)下載-帶進(jìn)度條和百分比進(jìn)度顯示效果
下面小編就為大家?guī)硪黄猘ndroid多線程斷點(diǎn)下載-帶進(jìn)度條和百分比進(jìn)度顯示效果。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06ViewFlipper實(shí)現(xiàn)文字輪播效果
這篇文章主要為大家詳細(xì)介紹了ViewFlipper實(shí)現(xiàn)文字輪播效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08Android開發(fā)使用自定義View將圓角矩形繪制在Canvas上的方法
這篇文章主要介紹了Android開發(fā)使用自定義View將圓角矩形繪制在Canvas上的方法,結(jié)合實(shí)例形式分析了Android自定義view繪制圓角矩形的相關(guān)方法與使用技巧,需要的朋友可以參考下2017-10-10詳解Android開發(fā)中Activity的四種launchMode
這篇文章主要介紹了Android開發(fā)中Activity的四種launchMode,launchMode主要用于控制多個(gè)Activity間的跳轉(zhuǎn),需要的朋友可以參考下2016-03-03Android MPAndroidChart開源庫圖表之折線圖的實(shí)例代碼
這篇文章主要介紹了Android MPAndroidChart開源庫圖表之折線圖的實(shí)例代碼,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05