一. 什么是ViewModel
官方對ViewModel的定義:
1、類職責:負責為界面準備數(shù)據(jù)(意味著一切處理數(shù)據(jù)邏輯的業(yè)務代碼钾唬,應該寫在ViewModel中)
2、在配置更改期間會自動保留ViewModel對象:因此可以作為跨頁面(Fragment)通訊的基石
二. ViewModel有什么優(yōu)點
- Activity配置更改重建時(比如屏幕旋轉(zhuǎn))保留數(shù)據(jù)
- UI組件(Activity與Fragment、Fragment與Fragment)間實現(xiàn)數(shù)據(jù)共享
對于第一條不用VM的情況下只能通過onSaveInstanceState保存數(shù)據(jù)抡秆,當activity重建后再通過onCreate或onRestoreInstanceState方法的bundle中取出奕巍,但如果數(shù)據(jù)量較大,數(shù)據(jù)的序列化和反序列化將產(chǎn)生一定的性能開銷儒士。
對于第二條如果不用VM的止,各個UI組件都要持有共享數(shù)據(jù)的引用,這會帶來兩個麻煩着撩,第一诅福,如果新增了共享數(shù)據(jù),各個UI組件需要再次聲明并初始化新增的共享數(shù)據(jù)拖叙;第二氓润,某個UI組件對共享數(shù)據(jù)修改,無法直接通知其他UI組件薯鳍,需手動實現(xiàn)觀察者模式咖气。而VM結(jié)合LiveData就可以很輕松的實現(xiàn)這一點。
知道了它的優(yōu)化挖滤,下面我們來看看如果使用它
三. 如何使用
首先寫個類崩溪,繼承 ViewModel
class UserViewModel : ViewModel() {
private var users: MutableLiveData<String>? = null
fun getUsers(): LiveData<String>? {
if (users == null) {
users = MutableLiveData<String>()
users?.value = "hello world"
}
return users
}
}
然后后Activity里面使用
class ViewModelActivity : AppCompatActivity() {
lateinit var viewmodel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_model)
setTitle("ViewModel的使用")
//通過ViewModelProvider獲取實例
viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
viewModel.getUsers()?.observe(this, Observer {
Log.d("ViewModel", it)
})
}
}
如果引入了依賴庫
implementation "androidx.fragment:fragment-ktx:1.2.4"
那么上述實例化ViewModel可以通過by關(guān)鍵字方便實現(xiàn)
private val viewModel by viewModels<MyViewModel>()
// Activity中如果共享的ViewModel
private val viewModel by activityViewModels<MyViewModel>()
activityViewModels是什么呢?如果Activity中有好幾個Fragment斩松,那么這幾個Fragment通過activityViewModels方法拿到的ViewModel都是同一樣伶唯。這樣就實現(xiàn)了數(shù)據(jù)的共享
當然viewModels和activityViewModels也是通過ViewModelProvider(this).get方法創(chuàng)建的。所以我們看源碼惧盹,也是從ViewModelProvider(this).get方法看是如果生成對象的乳幸。通過源碼就可以清楚的知道為什么它會有那兩個優(yōu)點了
3調(diào)用源碼分析
構(gòu)造方法
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
ViewModelProvider的構(gòu)造方法傳入的是一個ViewModelStoreOwner,獲取的是owner.getViewModelStore()方法岭参,getViewModelStore返回的是一個ViewModelStore對象反惕。而ViewModelStore對象就比較簡單,里面就主要有個Map<String,ViewModel>存儲ViewModel演侯。
看到這里可以猜測,我們獲取的ViewModel存儲應該就是在這個ViewModelStore里面背亥,類似于緩存一樣
ViewModelProvider(this)方法傳的是當前Activity對象秒际,那么肯定是哪個父類繼承了ViewModelStoreOwner接口。往上找發(fā)現(xiàn)ComponentActivity實現(xiàn)了這個接口
在ComponentActivity里面有個mViewModelStore
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
...{
private ViewModelStore mViewModelStore;
}
里面實現(xiàn)了getViewModelStore方法狡汉,這里是個關(guān)鍵點娄徊,可以看出來,如果mViewModelStore 為null盾戴,則去NonConfigurationInstances 里面拿寄锐,那這個getLastNonConfigurationInstance從哪里拿的?這里我們暫且不跟,先看它是如果創(chuàng)建ViewModel的橄仆,如果前面獲取都為null剩膘,最后是直接new了一個出來
public ViewModelStore getViewModelStore() {
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;
}
繼續(xù)看ViewModelProvider#get方法
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
通過類名和前綴DEFAULT_KEY 生成一個字符串key,繼續(xù)調(diào)用重載方法盆顾,從前面mViewModelStore里面拿出來一個ViewModel怠褐,如果不為空,則直接返回您宪,可見mViewModelStore確實就是緩存用的奈懒,如果為空。則通過mFactory 創(chuàng)建一個宪巨。并且放入mViewModelStore
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.
}
}
//通過factory創(chuàng)建viewModel
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
//存入mViewModelStore
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
這個代碼也好理解磷杏,就是從mViewModelStore里面拿對象,拿不到就讓factory去生成一個捏卓。由于我們沒有傳入factory,這里的factory就是默認的NewInstanceFactory极祸。而NewInstanceFactory邏輯也簡單,也就是直接反射生成一個對象天吓。
分析到這我們發(fā)現(xiàn)贿肩,ViewModelProvider.get方法其實邏輯就是反射生成一個ViewMode,并存儲到當前Activity的mViewModelStore中,
那么問題來了龄寞,既然旋轉(zhuǎn)屏幕汰规,Activity會重新創(chuàng)建,那么它里面的變量mViewModelStore怎么還能保持是同一個物邑?
答案就在剛剛的ComponentActivity#getViewModelStore方法中溜哮,繼續(xù)看這個方法,這里的mViewModelStore并不是重新new色解,而是從一個NonConfigurationInstances 對象中去拿茂嗓,而這個getLastNonConfigurationInstance方法那就是這個問題的關(guān)鍵所在了
public ViewModelStore getViewModelStore() {
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;
}
我們繼續(xù)看這個
getLastNonConfigurationInstance方法
按ctrl鍵點進去,進了Activity里面了
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
我們關(guān)心的是這個對象是在哪里賦值的科阎,在attach方法里面發(fā)現(xiàn)了賦值的地方
final void attach(...) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
....
mLastNonConfigurationInstances = lastNonConfigurationInstances;
}
那么attach方法里面的這個lastNonConfigurationInstances又是從哪里來的呢述吸?
在ActivityThread里面的performLaunchActivity方法,創(chuàng)建時會調(diào)用這個方法綁定一些重要的數(shù)據(jù)到activity锣笨,
現(xiàn)在我們知道lastNonConfigurationInstances是存在ActivityClientRecord中的蝌矛。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
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);
...
return activity;
}
那么r.lastNonConfigurationInstances又是在哪里賦值的呢?
同樣的
ActivityThread#performDestroyActivity方法中我們看到了賦值操作错英。如果發(fā)生了配置變化入撒,會調(diào)用activity的retainNonConfigurationInstances方法,存入ActivityClientRecord中
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
...
if (getNonConfigInstance) {
try {
//如果發(fā)生了配置改變椭岩,數(shù)據(jù)存起來
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;
}
似乎離真相越來越近了茅逮,璃赡,我們繼續(xù)看retainNonConfigurationInstances方法
Activity#retainNonConfigurationInstances
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
...
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity;
nci.children = children;
nci.fragments = fragments;
nci.loaders = loaders;
if (mVoiceInteractor != null) {
mVoiceInteractor.retainInstance();
nci.voiceInteractor = mVoiceInteractor;
}
return nci;
}
里面回調(diào)了onRetainNonConfigurationInstance方法,而ComponentActivity復寫了這個方法献雅,把viewModelStore 存入了NonConfigurationInstances
ComponentActivity#onRetainNonConfigurationInstance方法碉考,把viewModelStore存在NonConfigurationInstances中
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;
}
至此,總結(jié)下流程:
當Activity配置發(fā)生了變化惩琉,執(zhí)行了ActivityThread#performDestroyActivity方法時豆励,會把ViewModelStore存儲在NonConfigurationInstances ,然后把NonConfigurationInstances 存儲在ActivityClientRecord瞒渠。
然后重啟Activity時良蒸,會在attach方法,把ActivityClientRecord的NonConfigurationInstances給Activity