Jetpack AAC 系列文章:
“終于懂了“系列:Jetpack AAC完整解析(一)Lifecycle 完全掌握珍坊!
“終于懂了“系列:Jetpack AAC完整解析(二)LiveData 完全掌握!
“終于懂了“系列:Jetpack AAC完整解析(三)ViewModel 完全掌握正罢!
上一篇介紹了Jetpack AAC 的數(shù)據(jù)處理組件 LiveData翻具,它是使得 數(shù)據(jù)的更新 能以觀察者模式 被observer感知履怯,且此感知只發(fā)生在活躍生命周期狀態(tài)。 這篇來介紹與LiveData搭配使用的視圖模型組件——ViewModel呛占。
注意虑乖,如果你對MVVM架構(gòu)中的VM和本篇的ViewModel都沒有一定認識的話,那么就不要將兩者進行聯(lián)想了晾虑。目前疹味,你就理解為沒有任何關系。后面會有專門篇幅介紹MVVM帜篇。
一糙捺、ViewModel介紹
ViewModel是Jetpack AAC的重要組件,同時也有一個同名抽象類笙隙。
ViewModel洪灯,意為 視圖模型,即 為界面準備數(shù)據(jù)的模型竟痰。簡單理解就是签钩,ViewModel為UI層提供數(shù)據(jù)。
官方文檔定義如下:
ViewModel 以注重生命周期的方式存儲和管理界面相關的數(shù)據(jù)坏快。(作用)
ViewModel 類讓數(shù)據(jù)可在發(fā)生屏幕旋轉(zhuǎn)等配置更改后繼續(xù)留存铅檩。(特點)
到這里,你可能還是不清楚ViewModel到底是干啥的莽鸿,別急昧旨,往下看拾给。
1.1 出場背景
在詳細介紹ViewModel前,先來看下背景和問題點兔沃。
Activity可能會在某些場景(例如屏幕旋轉(zhuǎn))銷毀和重新創(chuàng)建界面蒋得,那么存儲在其中的界面相關數(shù)據(jù)都會丟失。例如乒疏,界面含用戶信息列表额衙,因配置更改而重新創(chuàng)建 Activity 后,新 Activity 必須重新請求用戶列表怕吴,這會造成資源的浪費入偷。能否直接恢復之前的數(shù)據(jù)呢?對于簡單的數(shù)據(jù)械哟,Activity 可以使用 onSaveInstanceState() 方法保存 然后從 onCreate() 中的Bundle恢復數(shù)據(jù),但此方法僅適合可以序列化再反序列化的少量數(shù)據(jù)(IPC對Bundle有1M的限制)殿雪,而不適合數(shù)量可能較大的數(shù)據(jù)暇咆,如用戶信息列表或位圖。 那么如何做到 因配置更改而新建Activity后的數(shù)據(jù)恢復呢丙曙?
UI層(如 Activity 和 Fragment)經(jīng)常需要通過邏輯層(如MVP中的Presenter)進行異步請求爸业,可能需要一些時間才能返回結(jié)果,如果邏輯層持有UI層應用(如context)亏镰,那么UI層需要管理這些請求扯旷,確保界面銷毀后清理這些調(diào)用以避免潛在的內(nèi)存泄露,但此項管理需要大量的維護工作索抓。 那么如何更好的避免因異步請求帶來的內(nèi)存泄漏呢钧忽?
這時候ViewModel就閃亮出場了——ViewModel用于代替MVP中的Presenter,為UI層準備數(shù)據(jù)逼肯,用于解決上面兩個問題耸黑。
1.2 特點
具體地,相比于Presenter篮幢,ViewModel有以下特點:
1.2.1 生命周期長于Activity
ViewModel最重要的特點是 生命周期長于Activity大刊。來看下官網(wǎng)的一張圖:
看到在因屏幕旋轉(zhuǎn)而重新創(chuàng)建Activity后,ViewModel對象依然會保留三椿。 只有Activity真正Finish的時ViewModel才會被清除缺菌。
也就是說,因系統(tǒng)配置變更Activity銷毀重建搜锰,ViewModel對象會保留并關聯(lián)到新的Activity伴郁。而Activity的正常銷毀(系統(tǒng)不會重建Activity)時,ViewModel對象是會清除的纽乱。
那么很自然的蛾绎,因系統(tǒng)配置變更Activity銷毀重建,ViewModel內(nèi)部存儲的數(shù)據(jù) 就可供重新創(chuàng)建的Activity實例使用了。這就解決了第一個問題租冠。
1.2.2 不持有UI層引用
我們知道鹏倘,在MVP的Presenter中需要持有IView接口來回調(diào)結(jié)果給界面。
而ViewModel是不需要持有UI層引用的顽爹,那結(jié)果怎么給到UI層呢纤泵?答案就是使用上一篇中介紹的基于觀察者模式的LiveData。 并且镜粤,ViewModel也不能持有UI層引用捏题,因為ViewModel的生命周期更長。
所以肉渴,ViewModel不需要也不能 持有UI層引用公荧,那么就避免了可能的內(nèi)存泄漏,同時實現(xiàn)了解耦同规。這就解決了第二個問題循狰。
二、ViewModel使用
2.1 基本使用
了解了ViewModel作用解特點券勺,下面來看看如何結(jié)合LivaData使用的绪钥。(gradle依賴在第一篇中已經(jīng)介紹過了。)
步驟:
- 繼承ViewModel自定義MyViewModel
- 在MyViewModel中編寫獲取UI數(shù)據(jù)的邏輯
- 使用LiveData將獲取到的UI數(shù)據(jù)拋出
- 在Activity/Fragment中使用ViewModelProvider獲取MyViewModel實例
- 觀察MyViewModel中的LiveData數(shù)據(jù)关炼,進行對應的UI更新程腹。
舉個例子,如果您需要在Activity中顯示用戶信息儒拂,那么需要將獲取用戶信息的操作分放到ViewModel中寸潦,代碼如下:
public class UserViewModel extends ViewModel {
private MutableLiveData<String> userLiveData ;
private MutableLiveData<Boolean> loadingLiveData;
public UserViewModel() {
userLiveData = new MutableLiveData<>();
loadingLiveData = new MutableLiveData<>();
}
//獲取用戶信息,假裝網(wǎng)絡請求 2s后 返回用戶信息
public void getUserInfo() {
loadingLiveData.setValue(true);
new AsyncTask<Void, Void, String>() {
@Override
protected void onPostExecute(String s) {
loadingLiveData.setValue(false);
userLiveData.setValue(s);//拋出用戶信息
}
@Override
protected String doInBackground(Void... voids) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String userName = "我是胡飛洋,公眾號名字也是胡飛洋社痛,歡迎關注~";
return userName;
}
}.execute();
}
public LiveData<String> getUserLiveData() {
return userLiveData;
}
public LiveData<Boolean> getLoadingLiveData() {
return loadingLiveData;
}
}
UserViewModel繼承ViewModel甸祭,然后邏輯很簡單:假裝網(wǎng)絡請求 2s后 返回用戶信息,其中userLiveData用于拋出用戶信息褥影,loadingLiveData用于控制進度條顯示池户。
再看UI層:
public class UserActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
Log.i(TAG, "onCreate: ");
TextView tvUserName = findViewById(R.id.textView);
ProgressBar pbLoading = findViewById(R.id.pb_loading);
//獲取ViewModel實例
ViewModelProvider viewModelProvider = new ViewModelProvider(this);
UserViewModel userViewModel = viewModelProvider.get(UserViewModel.class);
//觀察 用戶信息
userViewModel.getUserLiveData().observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
// update ui.
tvUserName.setText(s);
}
});
userViewModel.getLoadingLiveData().observe(this, new Observer<Boolean>() {
@Override
public void onChanged(Boolean aBoolean) {
pbLoading.setVisibility(aBoolean?View.VISIBLE:View.GONE);
}
});
//點擊按鈕獲取用戶信息
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
userViewModel.getUserInfo();
}
});
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "onStop: ");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy: ");
}
}
頁面有個按鈕用于點擊獲取用戶信息,有個TextView展示用戶信息。 在onCreate()中先 創(chuàng)建ViewModelProvider實例,傳入的參數(shù)是ViewModelStoreOwner翔试,Activity和Fragment都是其實現(xiàn)粒梦。然后通過ViewModelProvider的get方法 獲取ViewModel實例,然后就是 觀察ViewModel中的LiveData。
運行后,點擊按鈕 會彈出進度條,2s后展示用戶信息耸成。接著旋轉(zhuǎn)手機报亩,我們發(fā)現(xiàn)用戶信息依然存在。來看下效果:
旋轉(zhuǎn)手機后確實是重建了Activity的井氢,日志打印如下:
2021-01-06 20:35:44.984 28269-28269/com.hfy.androidlearning I/UserActivity: onStop:
2021-01-06 20:35:44.986 28269-28269/com.hfy.androidlearning I/UserActivity: onDestroy:
2021-01-06 20:35:45.025 28269-28269/com.hfy.androidlearning I/UserActivity: onCreate:
總結(jié)下:
- ViewModel的使用很簡單弦追,作用和原來的Presenter一致。只是要結(jié)合LiveData花竞,UI層觀察即可劲件。
- ViewModel的創(chuàng)建必須通過ViewModelProvider。
- 注意到ViewModel中沒有持有任何UI相關的引用约急。
- 旋轉(zhuǎn)手機重建Activity后零远,數(shù)據(jù)確實恢復了。
2.2 Fragment間數(shù)據(jù)共享
Activity 中的多個Fragment需要相互通信是一種很常見的情況厌蔽。假設有一個ListFragment牵辣,用戶從列表中選擇一項,會有另一個DetailFragment顯示選定項的詳情內(nèi)容奴饮。在之前 你可能會定義接口或者使用EventBus來實現(xiàn)數(shù)據(jù)的傳遞共享服猪。
現(xiàn)在就可以使用 ViewModel 來實現(xiàn)。這兩個 Fragment 可以使用其 Activity 范圍共享 ViewModel 來處理此類通信拐云,如以下示例代碼所示:
//ViewModel
public class SharedViewModel extends ViewModel {
//被選中的Item
private final MutableLiveData<UserContent.UserItem> selected = new MutableLiveData<UserContent.UserItem>();
public void select(UserContent.UserItem user) {
selected.setValue(user);
}
public LiveData<UserContent.UserItem> getSelected() {
return selected;
}
}
//ListFragment
public class MyListFragment extends Fragment {
...
private SharedViewModel model;
...
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//獲取ViewModel,注意ViewModelProvider實例傳入的是宿主Activity
model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
adapter.setListner(new MyItemRecyclerViewAdapter.ItemCLickListner(){
@Override
public void onClickItem(UserContent.UserItem userItem) {
model.select(userItem);
}
});
}
}
//DetailFragment
public class DetailFragment extends Fragment {
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
TextView detail = view.findViewById(R.id.tv_detail);
//獲取ViewModel,觀察被選中的Item
SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
model.getSelected().observe(getViewLifecycleOwner(), new Observer<UserContent.UserItem>() {
@Override
public void onChanged(UserContent.UserItem userItem) {
//展示詳情
detail.setText(userItem.toString());
}
});
}
}
代碼很簡單近她,ListFragment中在點擊Item時更新ViewModel的LiveData數(shù)據(jù)叉瘩,然后DetailFragment監(jiān)聽這個LiveData數(shù)據(jù)即可。
要注意的是粘捎,這兩個 Fragment 通過ViewModelProvider獲取ViewModel時 傳入的都是它們宿主Activity薇缅。這樣,當這兩個 Fragment 各自獲取 ViewModelProvider 時攒磨,它們會收到相同的 SharedViewModel 實例(其范圍限定為該 Activity)泳桦。
此方法具有以下 優(yōu)勢:
- Activity 不需要執(zhí)行任何操作,也不需要對此通信有任何了解娩缰。
- 除了 SharedViewModel 約定之外灸撰,F(xiàn)ragment 不需要相互了解。如果其中一個 Fragment 消失拼坎,另一個 Fragment 將繼續(xù)照常工作浮毯。
- 每個 Fragment 都有自己的生命周期,而不受另一個 Fragment 的生命周期的影響泰鸡。如果一個 Fragment 替換另一個 Fragment债蓝,界面將繼續(xù)工作而沒有任何問題。
最后來看下效果:
三盛龄、源碼分析
經(jīng)過前面的介紹饰迹,我們知道ViewModel的核心點 就是 因配置更新而界面(Activity/Fragment)重建后芳誓,ViewModel實例依然存在,這個如何實現(xiàn)的呢啊鸭? 這就是我們源碼分析的重點了锹淌。
在獲取ViewModel實例時,我們并不是直接new的莉掂,而是使用ViewModelProvider來獲取葛圃,猜測關鍵點應該就在這里了。
3.1 ViewModel的存儲和獲取
先來看下ViewModel類:
public abstract class ViewModel {
...
private volatile boolean mCleared = false;
//在ViewModel將被清除時調(diào)用
//當ViewModel觀察了一些數(shù)據(jù)憎妙,可以在這里做解注冊 防止內(nèi)存泄漏
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
@MainThread
final void clear() {
mCleared = true;
...
onCleared();
}
...
}
ViewModel類 是抽象類库正,內(nèi)部沒有啥邏輯,有個clear()方法會在ViewModel將被清除時調(diào)用厘唾。
然后ViewModel實例的獲取是通過ViewModelProvider類褥符,見名知意,即ViewModel提供者抚垃,來看下它的構(gòu)造方法:
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;
}
例子中我們使用的是只需傳ViewModelStoreOwner的構(gòu)造方法喷楣,最后走到兩個參數(shù)ViewModelStore、factory的構(gòu)造方法鹤树。繼續(xù)見名知意:ViewModelStoreOwner——ViewModel存儲器擁有者铣焊;ViewModelStore——ViewModel存儲器,用來存ViewModel的地方罕伯;Factory——創(chuàng)建ViewModel實例的工廠曲伊。
ViewModelStoreOwner是個接口:
public interface ViewModelStoreOwner {
//獲取ViewModelStore,即獲取ViewModel存儲器
ViewModelStore getViewModelStore();
}
實現(xiàn)類有Activity/Fragment追他,也就是說 Activity/Fragment 都是 ViewModel存儲器的擁有者坟募,具體是怎樣實現(xiàn) 獲取ViewModelStore的呢?
先不急邑狸,我們先看 ViewModelStore 如何存儲ViewModel懈糯、以及ViewModel實例如何獲取的。
/**
* 用于存儲ViewModels.
* ViewModelStore實例 必須要能 在系統(tǒng)配置改變后 依然存在单雾。
*/
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());
}
/**
* 調(diào)用ViewModel的clear()方法赚哗,然后清除ViewModel
* 如果ViewModelStore的擁有者(Activity/Fragment)銷毀后不會重建,那么就需要調(diào)用此方法
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
ViewModelStore代碼很簡單硅堆,viewModel作為Value存儲在HashMap中蜂奸。
再來看下創(chuàng)建ViewModel實例的工廠Factory,也就是NewInstanceFactory:
public static class NewInstanceFactory implements Factory {
...
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
}
很簡單硬萍,就是通過傳入的class 反射獲取ViewModel實例扩所。
回到例子中,我們使用viewModelProvider.get(UserViewModel.class)
來獲取UserViewModel實例朴乖,那么來看下get()方法:
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");
//拿到Key祖屏,也即是ViewModelStore中的Map的用于存 ViewModel的 Key
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//從ViewModelStore獲取ViewModel實例
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
//如果從ViewModelStore獲取到助赞,直接返回
return (T) viewModel;
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
//沒有獲取到,就使用Factory創(chuàng)建
viewModel = (mFactory).create(modelClass);
}
//存入ViewModelStore 然后返回
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
邏輯很清晰袁勺,先嘗試從ViewModelStore獲取ViewModel實例雹食,key是"androidx.lifecycle.ViewModelProvider.DefaultKey:xxx.SharedViewModel",如果沒有獲取到期丰,就使用Factory創(chuàng)建群叶,然后存入ViewModelStore。
到這里钝荡,我們知道了 ViewModel如何存儲街立、實例如何獲取的,但開頭說的分析重點:“因配置更新而界面重建后埠通,ViewModel實例依然存在”赎离,這個還沒分析到。
3.2 ViewModelStore的存儲和獲取
回到上面的疑問端辱,看看 Activity/Fragment 是怎樣實現(xiàn) 獲取ViewModelStore的梁剔,先來看ComponentActivity中對ViewModelStoreOwner的實現(xiàn):
//ComponentActivity.java
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
//activity還沒關聯(lián)Application,即不能在onCreate之前去獲取viewModel
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mViewModelStore == null) {
//如果存儲器是空舞蔽,就先嘗試 從lastNonConfigurationInstance從獲取
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
//如果lastNonConfigurationInstance不存在荣病,就new一個
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
這里就是重點了。先嘗試 從NonConfigurationInstance從獲取 ViewModelStore實例渗柿,如果NonConfigurationInstance不存在个盆,就new一個mViewModelStore。 并且還注意到做祝,在onRetainNonConfigurationInstance()方法中 會把mViewModelStore賦值給NonConfigurationInstances:
//在Activity因配置改變 而正要銷毀時,且新Activity會立即創(chuàng)建鸡岗,那么系統(tǒng)就會調(diào)用此方法
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
...
if (viewModelStore == null && custom == null) {
return null;
}
//new了一個NonConfigurationInstances混槐,mViewModelStore賦值過來
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
onRetainNonConfigurationInstance()方法很重要:在Activity因配置改變 而正要銷毀時,且新Activity會立即創(chuàng)建轩性,那么系統(tǒng)就會調(diào)用此方法声登。 也就說,配置改變時 系統(tǒng)把viewModelStore存在了NonConfigurationInstances中揣苏。
NonConfigurationInstances是個啥呢悯嗓?
//ComponentActivity
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
ComponentActivity靜態(tài)內(nèi)部類,依然見名知意卸察,非配置實例脯厨,即 與系統(tǒng)配置 無關的 實例。所以屏幕旋轉(zhuǎn)等的配置改變 不會影響到這個實例坑质? 繼續(xù)看這個猜想是否正確合武。
我們看下getLastNonConfigurationInstance():
//Acticity.java
NonConfigurationInstances mLastNonConfigurationInstances;
//返回onRetainNonConfigurationInstance()返回的實例
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.activity : null;
}
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
方法是在Acticity.java中临梗,它返回的是Acticity.java中的NonConfigurationInstances的屬性activity,也就是onRetainNonConfigurationInstance()方法返回的實例稼跳。(注意上面那個是ComponentActivity中的NonConfigurationInstances盟庞,是兩個類)
來繼續(xù)看mLastNonConfigurationInstances是哪來的,通過尋找調(diào)用找到在attach()方法中:
final void attach(Context context, ActivityThread aThread, ...
NonConfigurationInstances lastNonConfigurationInstances,... ) {
...
mLastNonConfigurationInstances = lastNonConfigurationInstances;
...
}
mLastNonConfigurationInstances是在Activity的attach方法中賦值汤善。 在《Activity的啟動過程詳解》中我們分析過什猖,attach方法是為Activity關聯(lián)上下文環(huán)境,是在Activity 啟動的核心流程——ActivityThread的performLaunchActivity方法中調(diào)用红淡,這里的lastNonConfigurationInstances是存在 ActivityClientRecord中的一個組件信息不狮。
ActivityClientRecord是存在ActivityThread的mActivities中:
//ActivityThrtead.java
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
那么,ActivityThread 中的 ActivityClientRecord 是不受 activity 重建的影響锉屈,那么ActivityClientRecord中l(wèi)astNonConfigurationInstances也不受影響荤傲,那么其中的Object activity也不受影響,那么ComponentActivity中的NonConfigurationInstances的viewModelStore不受影響颈渊,那么viewModel也就不受影響了遂黍。
那么,到這里 核心問題 “配置更改重建后ViewModel依然存在” 的原理就分析完了俊嗽。
四雾家、對比onSaveInstanceState()
系統(tǒng)提供了onSaveInstanceState()用于讓開發(fā)者保存一些數(shù)據(jù),以方便界面銷毀重建時恢復數(shù)據(jù)绍豁。那么和 使用ViewModel恢復數(shù)據(jù) 有哪些區(qū)別呢芯咧?
4.1 使用場景
在我很久之前一篇文章《Activity生命周期》中有提到:
onSaveInstanceState調(diào)用時機:
當某個activity變得“容易”被系統(tǒng)銷毀時,該activity的onSaveInstanceState就會被執(zhí)行竹揍,除非該activity是被用戶主動銷毀的敬飒,例如當用戶按BACK鍵的時候。 注意上面的雙引號芬位,何為“容易”无拗?言下之意就是該activity還沒有被銷毀,而僅僅是一種可能性昧碉。
這種可能性有哪些英染?有這么幾種情況:
1、當用戶按下HOME鍵時被饿。 這是顯而易見的四康,系統(tǒng)不知道你按下HOME后要運行多少其他的程序,自然也不知道activity A是否會被銷毀狭握,故系統(tǒng)會調(diào)用onSaveInstanceState闪金,讓用戶有機會保存某些非永久性的數(shù)據(jù)。以下幾種情況的分析都遵循該原則 论颅。
2毕泌、長按HOME鍵喝检,選擇運行其他的程序時。
3撼泛、按下電源按鍵(關閉屏幕顯示)時挠说。
4、從activity A中啟動一個新的activity時愿题。
5损俭、屏幕方向切換時,例如從豎屏切換到橫屏時潘酗。 在屏幕切換之前杆兵,系統(tǒng)會銷毀activity A,在屏幕切換之后系統(tǒng)又會自動地創(chuàng)建activity A仔夺,所以onSaveInstanceState一定會被執(zhí)行琐脏。
總而言之,onSaveInstanceState的調(diào)用遵循一個重要原則缸兔,即當系統(tǒng)“未經(jīng)你許可”時銷毀了你的activity日裙,則onSaveInstanceState會被系統(tǒng)調(diào)用,這是系統(tǒng)的責任惰蜜,因為它必須要提供一個機會讓你保存你的數(shù)據(jù)(當然你不保存那就隨便你了)昂拂。
而使用ViewModel恢復數(shù)據(jù) 則 只有在 因配置更改界面銷毀重建 的情況。
4.2 存儲方式
ViewModel是存在內(nèi)存中抛猖,讀寫速度快格侯,而通過onSaveInstanceState是在 序列化到磁盤中。
4.3 存儲數(shù)據(jù)的限制
ViewModel财著,可以存復雜數(shù)據(jù)联四,大小限制就是App的可用內(nèi)存。而 onSaveInstanceState只能存可序列化和反序列化的對象撑教,且大小有限制(一般Bundle限制大小1M)朝墩。
五、總結(jié)
本文先介紹了ViewModel的概念——為界面準備數(shù)據(jù)的模型驮履,然后它的特點:因配置更改界面銷毀重建后依然存在鱼辙、不持有UI應用廉嚼;接著介紹了 使用方式玫镐、Fragment數(shù)據(jù)共享。最后詳細分析了ViewModel源碼及核心原理怠噪。
并且可以看到LiveData和ViewModel搭配使用恐似,可以代替MVP中的Presenter解決很多問題。ViewModel是我們后續(xù)建立MVVM架構(gòu)的重要組件傍念。 這也是我們必須掌握和理解的部分矫夷。
下一篇將介紹基于LifeCycle葛闷、LiveData、ViewModel的MVVM架構(gòu)双藕,終于要到MVVM了淑趾,敬請關注。
今天就到這里啦~
.
感謝與參考:
.
你的 點贊忧陪、評論扣泊,是對我的巨大鼓勵!