學習android的同學應該都了解Activity,關於Activity的相關介紹也有很多,下面簡要羅列出Activity的生命周期:
上圖列出了Activity在不同情況下的生命周期變化,內容來自Android開發藝術探索。
補充:
1. Activity之間的跳轉,比如A->B,生命周期的變化情況:
A: onPause -> B: onCreate -> B: onStart -> B: onResume -> A: onStop
由於A的onPause調用之后會創建B Activity,所以不適合在onPause中進行一些耗時操作,可以在onStop中進行操作。
2. Activity在資源配置發生改變以及被系統回收情況下的生命周期變化:
當Activity在異常情況下被銷毀時,其生命周期變化會略有不同,最常見的就是屏幕翻轉導致Activity銷毀重建,此時有些數據便會丟失,但某些數據不會,這是因為Activity在異常情況下被銷毀時,會調用onSaveInstanceState方法來保存一些狀態,也會調用View的onSaveInstanceState來保存View的一些狀態,而在恢復時會將保存的數據傳遞給onCreate和onRestoreInstanceState中的savedInstanceState,注意狀態保存方法只有在Activity在異常銷毀情況下才會調用,所以正常情況下是不能通過該方法來保存數據。onRestoreInstanceState一定會在onPostCreate之前調用,並在onStart之后調用,所以注意狀態恢復后的UI初始化。如果是由於資源配置的變化引起的銷毀重建,還會調用onConfigurationChanged方法,因此可在此方法中判斷資源發生了什么變化。
3. ViewModel為何能在屏幕翻轉Activity銷毀后保存狀態
當屏幕翻轉時,Activity會調用onDestroy進行銷毀,如果是正常情況下,ViewModel也應該被銷毀,但是ViewModel卻能夠保存其狀態,其本質是因為當Activity因資源配置變化重建時,ViewModel還是原來的ViewModel,並沒有被銷毀,接下來具體分析:
首先我們知道Activity實現了ViewModelStoreOwner接口,而從ViewModelStoreOwner中可以獲得ViewModelStore,ViewModelStore內部存在一個HashMap,用來保存所創建的ViewModel。
創建ViewModel方式:
/** * this表示Activity */ viewModel = ViewModelProvider(this)[ViewModel::class.java]
ViewModel的創建需要ViewModelStoreOwner,而Activity的父類繼承了ViewModelStoreOwner接口,所以可以直接傳入this,並且ViewModelProvider持有ViewModelStoreOwner的ViewModelStore,從以下代碼可以驗證:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) { this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory() : NewInstanceFactory.getInstance()); } public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) { this(owner.getViewModelStore(), factory); } public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) { mFactory = factory; mViewModelStore = store; }
由於ViewModelProvider持有ViewModelStore,所以便可以創建新的ViewModel並保存到ViewModelStore的HashMap中,或者從中取出ViewModel,在創建ViewModel時,便是調用ViewModelStore的get方法來獲取ViewModel,如果不存在,則創建:
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) { ViewModel viewModel = mViewModelStore.get(key); if (modelClass.isInstance(viewModel)) { if (mFactory instanceof OnRequeryFactory) { ((OnRequeryFactory) mFactory).onRequery(viewModel); } return (T) viewModel; } else { //noinspection StatementWithEmptyBody if (viewModel != null) { // TODO: log a warning. } } if (mFactory instanceof KeyedFactory) { viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass); } else { viewModel = (mFactory).create(modelClass); } mViewModelStore.put(key, viewModel); return (T) viewModel; }
到此,便產生了所需要的ViewModel。
當Activity銷毀時:
由於Activity實現了LifycycleOwner接口,所以當Activity生命周期有所變化時,便會通知到所有注冊的觀察者:
public ComponentActivity() { Lifecycle lifecycle = getLifecycle(); //noinspection ConstantConditions if (lifecycle == null) { throw new IllegalStateException("getLifecycle() returned null in ComponentActivity's " + "constructor. Please make sure you are lazily constructing your Lifecycle " + "in the first call to getLifecycle() rather than relying on field " + "initialization."); } if (Build.VERSION.SDK_INT >= 19) { getLifecycle().addObserver(new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { if (event == Lifecycle.Event.ON_STOP) { Window window = getWindow(); final View decor = window != null ? window.peekDecorView() : null; if (decor != null) { decor.cancelPendingInputEvents(); } } } }); } getLifecycle().addObserver(new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { if (event == Lifecycle.Event.ON_DESTROY) { if (!isChangingConfigurations()) { getViewModelStore().clear(); } } } }); if (19 <= SDK_INT && SDK_INT <= 23) { getLifecycle().addObserver(new ImmLeaksCleaner(this)); } }
由上面代碼可知,當Activity的生命周期狀態為ON_DESTROY時,會首先判斷是不是配置發生了改變,如果不是,則清空ViewModelStore中的HashMap。所以如果因為配置變化的原因導致Activity銷毀,其ViewModel並沒有銷毀。那如果沒有銷毀,當Activity重新創建時怎么找到它的呢?
其實當Activity因為配置改變而需要重現創建時,Activity還會調用onRetainNonConfigurationInstance方法,該方法在onStop和onDestroy之間調用,並且接下來要立即創建新的Activity實例,就是在這里將ViewModelStore保存了起來:
public final Object onRetainNonConfigurationInstance() { 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; }
既然保存起來了,那我們怎么獲取它的呢,其實答案就是在創建 ViewModel時來獲取的,還記得在創建ViewModel時,ViewModelProvider持有了ViewModelStore,而這個ViewModelStore是從ViewModelStoreOwner的getViewModelStore方法得來的,所以肯定是Activity在重寫此方法中做了處理:
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."); } if (mViewModelStore == null) { NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { // Restore the ViewModelStore from NonConfigurationInstances mViewModelStore = nc.viewModelStore; } if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } } return mViewModelStore; }
可以看到Activity首先是調用getlastNonConfigurationInstance方法來獲取ViewModelStore,而該方法就是取出之前onRetainNonConfigurationInstance方法所保存的數據,所以就可以成功的找到了之前所創建的ViewModel,也就可以恢復之前的狀態了。
總結:
ViewModel之所以能夠屏幕翻轉時保存狀態,是因為在銷毀之前保存了ViewModel(onRetainNonConfigurationInstance),並且在重新創建時取出之前的ViewModel(getViewModelStore)。onRetainNonConfigurationInstance方法只有在因為資源配置發生變化,Activity需要銷毀重建時才會調用,所以如果是因為內存不足而銷毀,則不會調用此方法,也就不能通過ViewModel的方式恢復之前的狀態,此時可以調用onSaveInstanceState方法來進行恢復,所以調用onRetainnonConfigurationinstance方法的條件要比onSaveInstanceState苛刻的多。