上一篇介紹了Jetpack AAC 的數(shù)據(jù)處理組件 LiveData,它是使得 數(shù)據(jù)的更新 能以觀察者模式 被observer感知你虹,且此感知只發(fā)生在活躍生命周期狀態(tài)贯底。 這篇來介紹與LiveData搭配使用的視圖模型組件——ViewModel休建。
注意袍镀,如果你對(duì)MVVM架構(gòu)中的VM和本篇的ViewModel都沒有一定認(rèn)識(shí)的話,那么就不要將兩者進(jìn)行聯(lián)想了辆琅。目前漱办,你就理解為沒有任何關(guān)系。后面會(huì)有專門篇幅介紹MVVM婉烟。
一娩井、ViewModel介紹
ViewModel是Jetpack AAC的重要組件,同時(shí)也有一個(gè)同名抽象類似袁。
ViewModel洞辣,意為 視圖模型,即 為界面準(zhǔn)備數(shù)據(jù)的模型叔营。簡單理解就是屋彪,ViewModel為UI層提供數(shù)據(jù)。 官方文檔定義如下:
ViewModel 以注重生命周期的方式存儲(chǔ)和管理界面相關(guān)的數(shù)據(jù)绒尊。(作用)
ViewModel 類讓數(shù)據(jù)可在發(fā)生屏幕旋轉(zhuǎn)等配置更改后繼續(xù)留存畜挥。(特點(diǎn))
到這里,你可能還是不清楚ViewModel到底是干啥的婴谱,別急蟹但,往下看。
1.1 出場背景
在詳細(xì)介紹ViewModel前谭羔,先來看下背景和問題點(diǎn)华糖。
Activity可能會(huì)在某些場景(例如屏幕旋轉(zhuǎn))銷毀和重新創(chuàng)建界面,那么存儲(chǔ)在其中的界面相關(guān)數(shù)據(jù)都會(huì)丟失瘟裸。例如客叉,界面含用戶信息列表,因配置更改而重新創(chuàng)建 Activity 后,新 Activity 必須重新請求用戶列表兼搏,這會(huì)造成資源的浪費(fèi)卵慰。能否直接恢復(fù)之前的數(shù)據(jù)呢?對(duì)于簡單的數(shù)據(jù)佛呻,Activity 可以使用 onSaveInstanceState() 方法保存 然后從 onCreate() 中的Bundle恢復(fù)數(shù)據(jù)裳朋,但此方法僅適合可以序列化再反序列化的少量數(shù)據(jù)(IPC對(duì)Bundle有1M的限制),而不適合數(shù)量可能較大的數(shù)據(jù)吓著,如用戶信息列表或位圖鲤嫡。 那么如何做到 因配置更改而新建Activity后的數(shù)據(jù)恢復(fù)呢?
UI層(如 Activity 和 Fragment)經(jīng)常需要通過邏輯層(如MVP中的Presenter)進(jìn)行異步請求绑莺,可能需要一些時(shí)間才能返回結(jié)果暖眼,如果邏輯層持有UI層應(yīng)用(如context),那么UI層需要管理這些請求紊撕,確保界面銷毀后清理這些調(diào)用以避免潛在的內(nèi)存泄露罢荡,但此項(xiàng)管理需要大量的維護(hù)工作赡突。 那么如何更好的避免因異步請求帶來的內(nèi)存泄漏呢对扶?
這時(shí)候ViewModel就閃亮出場了——ViewModel用于代替MVP中的Presenter,為UI層準(zhǔn)備數(shù)據(jù)惭缰,用于解決上面兩個(gè)問題浪南。
1.2 特點(diǎn)
具體地,相比于Presenter漱受,ViewModel有以下特點(diǎn):
1.2.1 生命周期長于Activity
ViewModel最重要的特點(diǎn)是 生命周期長于Activity络凿。來看下官網(wǎng)的一張圖:
看到在因屏幕旋轉(zhuǎn)而重新創(chuàng)建Activity后,ViewModel對(duì)象依然會(huì)保留昂羡。 只有Activity真正Finish的時(shí)ViewModel才會(huì)被清除絮记。
也就是說,因系統(tǒng)配置變更Activity銷毀重建虐先,ViewModel對(duì)象會(huì)保留并關(guān)聯(lián)到新的Activity怨愤。而Activity的正常銷毀(系統(tǒng)不會(huì)重建Activity)時(shí),ViewModel對(duì)象是會(huì)清除的蛹批。
那么很自然的撰洗,因系統(tǒng)配置變更Activity銷毀重建,ViewModel內(nèi)部存儲(chǔ)的數(shù)據(jù) 就可供重新創(chuàng)建的Activity實(shí)例使用了腐芍。這就解決了第一個(gè)問題差导。
1.2.2 不持有UI層引用
我們知道,在MVP的Presenter中需要持有IView接口來回調(diào)結(jié)果給界面猪勇。
而ViewModel是不需要持有UI層引用的设褐,那結(jié)果怎么給到UI層呢?答案就是使用上一篇中介紹的基于觀察者模式的LiveData。 并且助析,ViewModel也不能持有UI層引用裁替,因?yàn)閂iewModel的生命周期更長。
所以貌笨,ViewModel不需要也不能 持有UI層引用弱判,那么就避免了可能的內(nèi)存泄漏,同時(shí)實(shí)現(xiàn)了解耦锥惋。這就解決了第二個(gè)問題昌腰。
二、ViewModel使用
2.1 基本使用
了解了ViewModel作用解特點(diǎn)膀跌,下面來看看如何結(jié)合LivaData使用的遭商。(gradle依賴在第一篇中已經(jīng)介紹過了。)
步驟:
- 繼承ViewModel自定義MyViewModel
- 在MyViewModel中編寫獲取UI數(shù)據(jù)的邏輯
- 使用LiveData將獲取到的UI數(shù)據(jù)拋出
- 在Activity/Fragment中使用ViewModelProvider獲取MyViewModel實(shí)例
- 觀察MyViewModel中的LiveData數(shù)據(jù)捅伤,進(jìn)行對(duì)應(yīng)的UI更新劫流。
舉個(gè)例子,如果您需要在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)絡(luò)請求 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 = "我是胡飛洋,公眾號(hào)名字也是胡飛洋熄诡,歡迎關(guān)注~";
return userName;
}
}.execute();
}
public LiveData<String> getUserLiveData() {
return userLiveData;
}
public LiveData<Boolean> getLoadingLiveData() {
return loadingLiveData;
}
}
復(fù)制代碼
UserViewModel繼承ViewModel可很,然后邏輯很簡單:假裝網(wǎng)絡(luò)請求 2s后 返回用戶信息,其中userLiveData用于拋出用戶信息凰浮,loadingLiveData用于控制進(jìn)度條顯示我抠。
再看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實(shí)例
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);
}
});
//點(diǎn)擊按鈕獲取用戶信息
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: ");
}
}
復(fù)制代碼
頁面有個(gè)按鈕用于點(diǎn)擊獲取用戶信息,有個(gè)TextView展示用戶信息袜茧。 在onCreate()中先 創(chuàng)建ViewModelProvider實(shí)例菜拓,傳入的參數(shù)是ViewModelStoreOwner,Activity和Fragment都是其實(shí)現(xiàn)笛厦。然后通過ViewModelProvider的get方法 獲取ViewModel實(shí)例纳鼎,然后就是 觀察ViewModel中的LiveData。
運(yùn)行后递递,點(diǎn)擊按鈕 會(huì)彈出進(jìn)度條喷橙,2s后展示用戶信息。接著旋轉(zhuǎn)手機(jī)登舞,我們發(fā)現(xiàn)用戶信息依然存在贰逾。來看下效果:
旋轉(zhuǎn)手機(jī)后確實(shí)是重建了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:
復(fù)制代碼
總結(jié)下:
- ViewModel的使用很簡單菠秒,作用和原來的Presenter一致疙剑。只是要結(jié)合LiveData氯迂,UI層觀察即可。
- ViewModel的創(chuàng)建必須通過ViewModelProvider言缤。
- 注意到ViewModel中沒有持有任何UI相關(guān)的引用嚼蚀。
- 旋轉(zhuǎn)手機(jī)重建Activity后,數(shù)據(jù)確實(shí)恢復(fù)了管挟。
2.2 Fragment間數(shù)據(jù)共享
Activity 中的多個(gè)Fragment需要相互通信是一種很常見的情況轿曙。假設(shè)有一個(gè)ListFragment,用戶從列表中選擇一項(xiàng)僻孝,會(huì)有另一個(gè)DetailFragment顯示選定項(xiàng)的詳情內(nèi)容导帝。在之前 你可能會(huì)定義接口或者使用EventBus來實(shí)現(xiàn)數(shù)據(jù)的傳遞共享。
現(xiàn)在就可以使用 ViewModel 來實(shí)現(xiàn)穿铆。這兩個(gè) 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實(shí)例傳入的是宿主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());
}
});
}
}
復(fù)制代碼
代碼很簡單荞雏,ListFragment中在點(diǎn)擊Item時(shí)更新ViewModel的LiveData數(shù)據(jù)虐秦,然后DetailFragment監(jiān)聽這個(gè)LiveData數(shù)據(jù)即可。
要注意的是凤优,這兩個(gè) Fragment 通過ViewModelProvider獲取ViewModel時(shí) 傳入的都是它們宿主Activity悦陋。這樣,當(dāng)這兩個(gè) Fragment 各自獲取 ViewModelProvider 時(shí)别洪,它們會(huì)收到相同的 SharedViewModel 實(shí)例(其范圍限定為該 Activity)叨恨。
此方法具有以下 優(yōu)勢:
- Activity 不需要執(zhí)行任何操作,也不需要對(duì)此通信有任何了解挖垛。
- 除了 SharedViewModel 約定之外,F(xiàn)ragment 不需要相互了解秉颗。如果其中一個(gè) Fragment 消失痢毒,另一個(gè) Fragment 將繼續(xù)照常工作。
- 每個(gè) Fragment 都有自己的生命周期蚕甥,而不受另一個(gè) Fragment 的生命周期的影響哪替。如果一個(gè) Fragment 替換另一個(gè) Fragment,界面將繼續(xù)工作而沒有任何問題菇怀。
最后來看下效果:
三凭舶、源碼分析
經(jīng)過前面的介紹,我們知道ViewModel的核心點(diǎn) 就是 因配置更新而界面(Activity/Fragment)重建后爱沟,ViewModel實(shí)例依然存在帅霜,這個(gè)如何實(shí)現(xiàn)的呢? 這就是我們源碼分析的重點(diǎn)了呼伸。
在獲取ViewModel實(shí)例時(shí)身冀,我們并不是直接new的,而是使用ViewModelProvider來獲取,猜測關(guān)鍵點(diǎn)應(yīng)該就在這里了搂根。
3.1 ViewModel的存儲(chǔ)和獲取
先來看下ViewModel類:
public abstract class ViewModel {
...
private volatile boolean mCleared = false;
//在ViewModel將被清除時(shí)調(diào)用
//當(dāng)ViewModel觀察了一些數(shù)據(jù)珍促,可以在這里做解注冊 防止內(nèi)存泄漏
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
@MainThread
final void clear() {
mCleared = true;
...
onCleared();
}
...
}
復(fù)制代碼
ViewModel類 是抽象類,內(nèi)部沒有啥邏輯剩愧,有個(gè)clear()方法會(huì)在ViewModel將被清除時(shí)調(diào)用猪叙。
然后ViewModel實(shí)例的獲取是通過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;
}
復(fù)制代碼
例子中我們使用的是只需傳ViewModelStoreOwner的構(gòu)造方法,最后走到兩個(gè)參數(shù)ViewModelStore五督、factory的構(gòu)造方法藏否。繼續(xù)見名知意:ViewModelStoreOwner——ViewModel存儲(chǔ)器擁有者;ViewModelStore——ViewModel存儲(chǔ)器充包,用來存ViewModel的地方副签;Factory——?jiǎng)?chuàng)建ViewModel實(shí)例的工廠。
ViewModelStoreOwner是個(gè)接口:
public interface ViewModelStoreOwner {
//獲取ViewModelStore基矮,即獲取ViewModel存儲(chǔ)器
ViewModelStore getViewModelStore();
}
復(fù)制代碼
實(shí)現(xiàn)類有Activity/Fragment淆储,也就是說 Activity/Fragment 都是 ViewModel存儲(chǔ)器的擁有者,具體是怎樣實(shí)現(xiàn) 獲取ViewModelStore的呢家浇?
先不急本砰,我們先看 ViewModelStore 如何存儲(chǔ)ViewModel、以及ViewModel實(shí)例如何獲取的钢悲。
/**
* 用于存儲(chǔ)ViewModels.
* ViewModelStore實(shí)例 必須要能 在系統(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)銷毀后不會(huì)重建莺琳,那么就需要調(diào)用此方法
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
復(fù)制代碼
ViewModelStore代碼很簡單还棱,viewModel作為Value存儲(chǔ)在HashMap中。
再來看下創(chuàng)建ViewModel實(shí)例的工廠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);
}
}
}
復(fù)制代碼
很簡單珍手,就是通過傳入的class 反射獲取ViewModel實(shí)例。
回到例子中辞做,我們使用viewModelProvider.get(UserViewModel.class)
來獲取UserViewModel實(shí)例琳要,那么來看下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實(shí)例
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;
}
復(fù)制代碼
邏輯很清晰,先嘗試從ViewModelStore獲取ViewModel實(shí)例嫂伞,key是"androidx.lifecycle.ViewModelProvider.DefaultKey:xxx.SharedViewModel"孔厉,如果沒有獲取到拯钻,就使用Factory創(chuàng)建,然后存入ViewModelStore撰豺。
到這里粪般,我們知道了 ViewModel如何存儲(chǔ)、實(shí)例如何獲取的污桦,但開頭說的分析重點(diǎn):“因配置更新而界面重建后亩歹,ViewModel實(shí)例依然存在”,這個(gè)還沒分析到凡橱。
3.2 ViewModelStore的存儲(chǔ)和獲取
回到上面的疑問小作,看看 Activity/Fragment 是怎樣實(shí)現(xiàn) 獲取ViewModelStore的,先來看ComponentActivity中對(duì)ViewModelStoreOwner的實(shí)現(xiàn):
//ComponentActivity.java
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
//activity還沒關(guān)聯(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) {
//如果存儲(chǔ)器是空顾稀,就先嘗試 從lastNonConfigurationInstance從獲取
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
//如果lastNonConfigurationInstance不存在,就new一個(gè)
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
復(fù)制代碼
這里就是重點(diǎn)了坝撑。先嘗試 從NonConfigurationInstance從獲取 ViewModelStore實(shí)例静秆,如果NonConfigurationInstance不存在,就new一個(gè)mViewModelStore巡李。 并且還注意到抚笔,在onRetainNonConfigurationInstance()方法中 會(huì)把mViewModelStore賦值給NonConfigurationInstances:
//在Activity因配置改變 而正要銷毀時(shí),且新Activity會(huì)立即創(chuàng)建侨拦,那么系統(tǒng)就會(huì)調(diào)用此方法
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
...
if (viewModelStore == null && custom == null) {
return null;
}
//new了一個(gè)NonConfigurationInstances殊橙,mViewModelStore賦值過來
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
復(fù)制代碼
onRetainNonConfigurationInstance()方法很重要:在Activity因配置改變 而正要銷毀時(shí),且新Activity會(huì)立即創(chuàng)建狱从,那么系統(tǒng)就會(huì)調(diào)用此方法膨蛮。 也就說,配置改變時(shí) 系統(tǒng)把viewModelStore存在了NonConfigurationInstances中矫夯。
NonConfigurationInstances是個(gè)啥呢鸽疾?
//ComponentActivity
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
復(fù)制代碼
ComponentActivity靜態(tài)內(nèi)部類,依然見名知意训貌,非配置實(shí)例,即 與系統(tǒng)配置 無關(guān)的 實(shí)例冒窍。所以屏幕旋轉(zhuǎn)等的配置改變 不會(huì)影響到這個(gè)實(shí)例递沪? 繼續(xù)看這個(gè)猜想是否正確。
我們看下getLastNonConfigurationInstance():
//Acticity.java
NonConfigurationInstances mLastNonConfigurationInstances;
//返回onRetainNonConfigurationInstance()返回的實(shí)例
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;
}
復(fù)制代碼
方法是在Acticity.java中综液,它返回的是Acticity.java中的NonConfigurationInstances的屬性activity款慨,也就是onRetainNonConfigurationInstance()方法返回的實(shí)例。(注意上面那個(gè)是ComponentActivity中的NonConfigurationInstances谬莹,是兩個(gè)類)
來繼續(xù)看mLastNonConfigurationInstances是哪來的檩奠,通過尋找調(diào)用找到在attach()方法中:
final void attach(Context context, ActivityThread aThread, ...
NonConfigurationInstances lastNonConfigurationInstances,... ) {
...
mLastNonConfigurationInstances = lastNonConfigurationInstances;
...
}
復(fù)制代碼
mLastNonConfigurationInstances是在Activity的attach方法中賦值桩了。 在《Activity的啟動(dòng)過程詳解》中我們分析過,attach方法是為Activity關(guān)聯(lián)上下文環(huán)境埠戳,是在Activity 啟動(dòng)的核心流程——ActivityThread的performLaunchActivity方法中調(diào)用井誉,這里的lastNonConfigurationInstances是存在 ActivityClientRecord中的一個(gè)組件信息。
ActivityClientRecord是存在ActivityThread的mActivities中:
//ActivityThrtead.java
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
復(fù)制代碼
那么整胃,ActivityThread 中的 ActivityClientRecord 是不受 activity 重建的影響颗圣,那么ActivityClientRecord中l(wèi)astNonConfigurationInstances也不受影響,那么其中的Object activity也不受影響屁使,那么ComponentActivity中的NonConfigurationInstances的viewModelStore不受影響在岂,那么viewModel也就不受影響了。
那么蛮寂,到這里 核心問題 “配置更改重建后ViewModel依然存在” 的原理就分析完了蔽午。
四、對(duì)比onSaveInstanceState()
系統(tǒng)提供了onSaveInstanceState()用于讓開發(fā)者保存一些數(shù)據(jù)酬蹋,以方便界面銷毀重建時(shí)恢復(fù)數(shù)據(jù)及老。那么和 使用ViewModel恢復(fù)數(shù)據(jù) 有哪些區(qū)別呢?
4.1 使用場景
在我很久之前一篇文章《Activity生命周期》中有提到:
onSaveInstanceState調(diào)用時(shí)機(jī):
當(dāng)某個(gè)activity變得“容易”被系統(tǒng)銷毀時(shí)除嘹,該activity的onSaveInstanceState就會(huì)被執(zhí)行写半,除非該activity是被用戶主動(dòng)銷毀的,例如當(dāng)用戶按BACK鍵的時(shí)候尉咕。 注意上面的雙引號(hào)叠蝇,何為“容易”?言下之意就是該activity還沒有被銷毀年缎,而僅僅是一種可能性悔捶。
這種可能性有哪些?有這么幾種情況:
1单芜、當(dāng)用戶按下HOME鍵時(shí)蜕该。 這是顯而易見的,系統(tǒng)不知道你按下HOME后要運(yùn)行多少其他的程序洲鸠,自然也不知道activity A是否會(huì)被銷毀堂淡,故系統(tǒng)會(huì)調(diào)用onSaveInstanceState,讓用戶有機(jī)會(huì)保存某些非永久性的數(shù)據(jù)扒腕。以下幾種情況的分析都遵循該原則 绢淀。
2、長按HOME鍵瘾腰,選擇運(yùn)行其他的程序時(shí)皆的。
3、按下電源按鍵(關(guān)閉屏幕顯示)時(shí)蹋盆。
4费薄、從activity A中啟動(dòng)一個(gè)新的activity時(shí)硝全。
5、屏幕方向切換時(shí)楞抡,例如從豎屏切換到橫屏?xí)r伟众。 在屏幕切換之前,系統(tǒng)會(huì)銷毀activity A拌倍,在屏幕切換之后系統(tǒng)又會(huì)自動(dòng)地創(chuàng)建activity A赂鲤,所以onSaveInstanceState一定會(huì)被執(zhí)行。
總而言之柱恤,onSaveInstanceState的調(diào)用遵循一個(gè)重要原則数初,即當(dāng)系統(tǒng)“未經(jīng)你許可”時(shí)銷毀了你的activity,則onSaveInstanceState會(huì)被系統(tǒng)調(diào)用梗顺,這是系統(tǒng)的責(zé)任泡孩,因?yàn)樗仨氁峁┮粋€(gè)機(jī)會(huì)讓你保存你的數(shù)據(jù)(當(dāng)然你不保存那就隨便你了)。
而使用ViewModel恢復(fù)數(shù)據(jù) 則 只有在 因配置更改界面銷毀重建 的情況寺谤。
4.2 存儲(chǔ)方式
ViewModel是存在內(nèi)存中仑鸥,讀寫速度快,而通過onSaveInstanceState是在 序列化到磁盤中变屁。
4.3 存儲(chǔ)數(shù)據(jù)的限制
ViewModel眼俊,可以存復(fù)雜數(shù)據(jù),大小限制就是App的可用內(nèi)存粟关。而 onSaveInstanceState只能存可序列化和反序列化的對(duì)象疮胖,且大小有限制(一般Bundle限制大小1M)。
五闷板、總結(jié)
本文先介紹了ViewModel的概念——為界面準(zhǔn)備數(shù)據(jù)的模型澎灸,然后它的特點(diǎn):因配置更改界面銷毀重建后依然存在、不持有UI應(yīng)用遮晚;接著介紹了 使用方式性昭、Fragment數(shù)據(jù)共享。最后詳細(xì)分析了ViewModel源碼及核心原理县遣。
并且可以看到LiveData和ViewModel搭配使用糜颠,可以代替MVP中的Presenter解決很多問題。ViewModel是我們后續(xù)建立MVVM架構(gòu)的重要組件萧求。 這也是我們必須掌握和理解的部分括蝠。
下一篇將介紹基于LifeCycle、LiveData饭聚、ViewModel的MVVM架構(gòu),終于要到MVVM了搁拙,敬請關(guān)注秒梳。
今天就到這里啦~
六法绵、分享
分享一份《Jetpack架構(gòu)組件從入門到精通》的pdf學(xué)習(xí)筆記給大家,內(nèi)容涵括了Jetpack幾乎所有你能想到的知識(shí)點(diǎn)酪碘,而每一個(gè)知識(shí)點(diǎn)都有詳細(xì)的源碼解析朋譬,以及實(shí)戰(zhàn)講解!