1、什么是ViewModel
ViewModel 具備宿主生命后期感知能力的數據存儲組件,可以理解為ViewModel可以用來存儲數據,而且在Activity因為異常銷毀重新創建,依舊存在。這個和Activity的onInstanceSave和onRestoreInstance有點相似。
ViewModel的數據是可以再Activity和Fragment中共享的。
2、ViewModel簡單使用
導入依賴
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0'
對于ViewModel,剛開始學習先去學習它的幾個特性即可。
1、可以在Activity,Fragment中共享數據。
2、可以在多個Activity中共享數據。
3、在異常的生命周期時依舊可以保存數據。
代碼中獲取ViewModel
// 1、初始化ViewModel
var myViewModel = ViewModelProvider(this).get(MyViewModel::class.java)
// 2、初始化ViewModel
var myViewModel = ViewModelProvider(viewModelStore,ViewModelProvider.NewInstanceFactory()).get(MyViewModel::class.java)
以上兩種方式都可以。初始化之后即可使用ViewModel中的數據了。
在Activity和Fragment中共享數據的不同初始化方法
在Activity中使用:
var myViewModel = ViewModelProvider(this).get(MyViewModel::class.java)
this代表activity或者application,如果是Application,那就代表這個viewmodel能在多個Activity中共享了。
ViewModel使用起來比較簡單,示例代碼我寫在最后。一般和LiveData聯用。
3、ViewModel的源碼分析
我們從獲取ViewModel的get方法說起。
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
// mViewModelStore 可以理解為一個保存VieModel的map,
// 通過key去獲取這個ViewMode
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
// 如果獲取到了這個ViewModel,那就直接返回
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
// 沒有獲取到這個ViewModel,那就通過key去生成一個新的ViewModel
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
通過這段代碼,我們需要知道
1、key如何定義;
2、viewmodel如何創建的;
@NonNull
@MainThread
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");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
可以看到這個方法,
1、會有一個DEFAULT_KEY+ ViewModle類的類名。
2、直接調用get(String key,Class<T> modelClass)這個方法,自定義一個key。
我們再看下如何創建這個ViewModel:
public interface Factory {
/**
* Creates a new instance of the given {@code Class}.
* <p>
*
* @param modelClass a {@code Class} whose instance is requested
* @param <T> The type parameter for the ViewModel.
* @return a newly created ViewModel
*/
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
在get方法中可以看到這個create方法。他是在一個Factory的接口,從這個名字可以看到,這個實際上是一個工廠模式。
實現類有這么多,那到底使用哪個呢?
再回到初始化ViewModelProvider這個方法
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
可以看到,這里會默認生成幾個Factory。源碼中包含兩個Factory的實現。NewInstanceFactory和AndroidViewModelFactory,可以簡單看下NewInstanceFactory
@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);
}
}
既然是工廠模式,那就都有不同的create方法實現,NewInstanceFactory就是直接通過class.newInstance的方法去實例化對象。
初步了解初始化之后,我們開始解決疑問:
1、這個東西為啥能在Activity和Fragment中共享數據?
初始化ViewModelProvider的方法。
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
實際上調用的是:
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
mViewModelStore 這個東西是從Activity中傳過來的。所以在Activity中是唯一的。
前面的get方法也可以看到,一旦這個ViewModel創建出來就會被放到mViewModelStore中去保存,下一次可以直接從mViewModelStore中拿出來。那么如果Fragment需要共享Activity中的數據,只需要在初始化的時候傳入Activity和相同的ViewModel的class即可。
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
var viewModel = ViewModelProvider(requireActivity()).get(MyViewModel::class.java)
}
這樣,拿到的mViewModelStore是Activity的,傳入的MyViewModel::class.java也會從mViewModelStore拿到已經存在的View Model,自然就可以實現數據的共享了。
2、這個東西為啥能在多個Activity中共享數據?
如果不看源碼,就可以直接理解為,這個ViewModel就是和Application的生命周期同步的,所有的Activity都能獲取到這個對象,自然也可以共享里面的數據。
1、使用AndroidViewModelFactory。如果使用AndroidViewModelFactory就可以在多個Activity中共享數據。
2、Application實現ViewModelStoreOwner,也可以做到在多個Activity中實現共享數據。
class BaseApplicaiton : Application(), ViewModelStoreOwner {
companion object{
var instance:BaseApplicaiton? = null
}
private val appViewModelStore: ViewModelStore by lazy {
ViewModelStore()
}
override fun onCreate() {
super.onCreate()
instance = this
}
override fun getViewModelStore(): ViewModelStore {
return appViewModelStore
}
}
3、ViewModel是如何在Activity異常生命周期恢復數據的?
保存數據可以看下ActivityThread的performDestoryActivity方法:
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
Class<? extends Activity> activityClass = null;
if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
if (r != null) {
activityClass = r.activity.getClass();
r.activity.mConfigChangeFlags |= configChanges;
if (finishing) {
r.activity.mFinished = true;
}
performPauseActivityIfNeeded(r, "destroy");
if (!r.stopped) {
callActivityOnStop(r, false /* saveState */, "destroy");
}
// 具體看這段代碼
if (getNonConfigInstance) {
try {
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to retain activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e);
}
}
}
// 省略若干代碼
return r;
}
retainNonConfigurationInstances這個方法就是將Activity的一些參數配置保存到NonConfigurationInstances 中。
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
// 省略若干代碼
return nci;
}
onRetainNonConfigurationInstance:這里主要就是將Activity的viewModelStore保存到NonConfigurationInstances。
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;
}
繼續追溯源碼getLastNonConfigurationInstance方法可以看到保存數據的NonConfigurationInstances.
/* package */ NonConfigurationInstances mLastNonConfigurationInstances;
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
Activity中有個ActivityClientRecord,可以理解為記錄Activity的的一些參數集合。里面就有 Activity.NonConfigurationInstances lastNonConfigurationInstances;從上面的代碼中可以看到,當Activity銷毀的時候這里就是將Activity的NonConfigurationInstances賦值給了ActivityClientRecord的NonConfigurationInstances。
如何還原數據可以看下ActivityThread的performLaunchActivity方法:
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken);
這里會將lastNonConfigurationInstances傳入,這里調用了activity的attach方法。通過這個方法將ActivityClientRecord的NonConfigurationInstances賦值給了Activity的NonConfigurationInstances。
4、ViewModel和onSaveInstance()的區別
1、onSaveInstance是通過Bundle保存數據的,保存可序列化數據,一般大小為1M-8k。
ViewModel可以保存任何形式的數據,大小不限制,不超過系統的App分配的內存即可。
2、onSaveInstance是將數據保存的磁盤中,而ViewModel是將數據保存在內存中。
5、總結
掌握ViewModel需要了解
1、ViewModel的基本用法。
2、ViewModel的數據共享原理。
3、ViewModel是如何做到Activity異常生命周期還能保存數據的。
4、ViewModel和onSaveInstance的一些區別。