原理
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class)
由于在Activity中調(diào)用耗啦,所以this值為Activity套菜,在Fragment中赐写,this則為Fragment,因此of肯定有多個(gè)構(gòu)造方法,以Activity中為例
源碼
ViewModelProviders.java的of
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
Application application = checkApplication(checkActivity(fragment));
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(fragment.getViewModelStore(), factory);
}
第2個(gè)of函數(shù)里,會(huì)調(diào)用getApplication方法來返回Activity對應(yīng)的Application
if語句中會(huì)創(chuàng)建AndroidViewModelFactory實(shí)例。最后會(huì)新建一個(gè)ViewModelProvider,將AndroidViewModelFactory作為參數(shù)傳入
ViewModelProvider.java的AndroidViewModelFactory
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static AndroidViewModelFactory sInstance;
/**
* Retrieve a singleton instance of AndroidViewModelFactory.
*
* @param application an application to pass in {@link AndroidViewModel}
* @return A valid {@link AndroidViewModelFactory}
*/
@NonNull
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
private Application mApplication;
/**
* Creates a {@code AndroidViewModelFactory}
*
* @param application an application to pass in {@link AndroidViewModel}
*/
public AndroidViewModelFactory(@NonNull Application application) {
mApplication = application;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
return super.create(modelClass);
}
}
可以看到塔嬉,AndroidViewModelFactory是一個(gè)單例,ViewModel本身是一個(gè)抽象類租悄,我們一般通過繼承ViewModel來實(shí)現(xiàn)自定義ViewModel谨究,那么AndroidViewModelFactory的create方法的作用就是通過反射生成ViewModel的實(shí)現(xiàn)類的
再來看看ViewModelProvider的get方法
ViewModelProvider.java的get
@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);
}
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
第1個(gè)get中,先拿到modelClass的類名泣棋,并對其進(jìn)行字符串拼接胶哲,作為第2個(gè)get的參數(shù),DEFAULT_KEY就是androidx.lifecycle.ViewModelProvider.DefaultKey
因此潭辈,第2個(gè)get方法中拿到的key就是DEFAULT_KEY + 類名鸯屿,根據(jù)key從ViewModelStore獲取ViewModel的實(shí)現(xiàn)類。如果ViewModel能直接轉(zhuǎn)成modelClass類的對象把敢,則直接返回該ViewModel寄摆,否則會(huì)通過Factory創(chuàng)建一個(gè)ViewModel,并將其存儲到ViewModelStore中修赞。這里的Factory指的就是AndroidViewModelFactory婶恼,在上面我們提到的ViewModelProvider創(chuàng)建時(shí)作為參數(shù)被傳進(jìn)來
關(guān)系解析
ViewModelProvider.java:為Fragment,Activity等提供ViewModels的utils類
ViewModelProviders.java:提供of方法榔组,返回一個(gè)ViewModelProvider
ViewModelStore.java:以HashMap形式緩存ViewModel熙尉,需要時(shí)從緩存中找,有則返回搓扯,無則創(chuàng)建
ViewModelStores.java:提供of方法,返回一個(gè)ViewModelStore給需要的Activity或Fragment
AndroidViewModelFactory:ViewModelProvider的靜態(tài)內(nèi)部類包归,提供create方法反射創(chuàng)建ViewModel實(shí)現(xiàn)類
如何在旋轉(zhuǎn)后保存數(shù)據(jù)
/**
* Retain all appropriate fragment state. You can NOT
* override this yourself! Use {@link #onRetainCustomNonConfigurationInstance()}
* if you want to retain your own state.
*/
@Override
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
if (fragments == null && mViewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = mViewModelStore;
nci.fragments = fragments;
return nci;
}
/**
* Use this instead of {@link #onRetainNonConfigurationInstance()}.
* Retrieve later with {@link #getLastCustomNonConfigurationInstance()}.
*/
public Object onRetainCustomNonConfigurationInstance() {
return null;
}
/**
* Return the value previously returned from
* {@link #onRetainCustomNonConfigurationInstance()}.
*/
@SuppressWarnings("deprecation")
public Object getLastCustomNonConfigurationInstance() {
NonConfigurationInstances nc = (NonConfigurationInstances)
getLastNonConfigurationInstance();
return nc != null ? nc.custom : null;
}
核心就在這里锨推,我們不可以重寫第一個(gè)方法,如果想要實(shí)現(xiàn)自己的保存數(shù)據(jù)方式需要重寫Custom方法,原生保存數(shù)據(jù)的流程是當(dāng)我們旋轉(zhuǎn)屏幕换可,系統(tǒng)會(huì)調(diào)用onRetainNonConfigurationInstance方法椎椰,在這個(gè)方法內(nèi)會(huì)將我們的ViewModelStore進(jìn)行保存。一旦當(dāng)前activity去獲取ViewModelStore沾鳄,會(huì)通過getLastNonConfigurationInstance方法恢復(fù)之前的ViewModelStore慨飘,所以狀態(tài)改變前后的ViewModelStore是同一個(gè)
為何傳入Context會(huì)內(nèi)存泄漏
ViewModel之所以不應(yīng)該包含Context的實(shí)例或類似于上下文的其他對象的原因是因?yàn)樗哂信cActivity和Fragment不同的生命周期。假設(shè)我們在應(yīng)用上進(jìn)行了更改译荞,這會(huì)導(dǎo)致Activity和Fragment自行銷毀瓤的,因此它會(huì)重新創(chuàng)建。ViewModel意味著在此狀態(tài)期間保持不變吞歼,因此如果它仍然在被破壞的Activity中持有View或Context圈膏,則可能會(huì)發(fā)生崩潰和其他異常
應(yīng)用舉例
MyViewModel
public class MyViewModel extends ViewModel {
public int num;
}
activity_main.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textSize="34sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.237" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="+"
android:textSize="34sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:onClick="AddNum"/>
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity
public class MainActivity extends AppCompatActivity {
TextView textView;
MyViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
//viewmodel中不要傳入context,如果必須要使用篙骡,換成AndroidViewModel里的Application
viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);
textView.setText(String.valueOf(viewModel.num));
}
//保存瞬態(tài)數(shù)據(jù)稽坤,屏幕旋轉(zhuǎn)后數(shù)據(jù)還在
public void AddNum(View view) {
textView.setText(String.valueOf(++viewModel.num));
}
}
這樣就簡單創(chuàng)建了一個(gè)Button和一個(gè)TextView用于顯示數(shù)字,在屏幕旋轉(zhuǎn)后數(shù)字并不會(huì)重置
配合LiveData
MyViewModel
public class MyViewModel extends ViewModel {
private MutableLiveData<Integer> currentSecond;
public MutableLiveData<Integer> getCurrentSecond(){
if(currentSecond == null){
currentSecond = new MutableLiveData<>();
currentSecond.setValue(0);
}
return currentSecond;
}
}
activity_data.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".DataActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
DataActivity
public class DataActivity extends AppCompatActivity {
private MyViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_data);
TextView textView = findViewById(R.id.textView);
viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);
textView.setText(String.valueOf(viewModel.getCurrentSecond().getValue()));
viewModel.getCurrentSecond().observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
textView.setText(String.valueOf(integer));
}
});
startTime();
}
private void startTime(){
new Timer().schedule(new TimerTask() {
@Override
public void run() {
//非UI線程糯俗,用postValue
//UI線程尿褪,用setValue
viewModel.getCurrentSecond().postValue(viewModel.getCurrentSecond().getValue() + 1);
}
},1000,1000);
}
這樣就創(chuàng)建了一個(gè)計(jì)時(shí)器,在屏幕旋轉(zhuǎn)后由于LiveData和ViewModel配合得湘,計(jì)時(shí)器并不會(huì)重置
總結(jié)
ViewModel的大體使用方式在于他調(diào)用了ViewModelProvider構(gòu)造器杖玲,構(gòu)造器中傳入的第一個(gè)參數(shù)是ViewModelStoreOwner,一般在Activity或者Fragment中直接傳this即可忽刽。第二個(gè)參數(shù)便是一個(gè)Factory對象天揖,而這個(gè)Factory對象用于創(chuàng)建ViewModel。然后調(diào)用get方法內(nèi)部構(gòu)造出一個(gè)key跪帝,用于從ViewModelStore中取出ViewModel
而旋轉(zhuǎn)后保存數(shù)據(jù)就通過onRetainNonConfigurationInstances方法構(gòu)建一個(gè)NonConfigurationInstance對象今膊,將此時(shí)的mViewModel對象設(shè)置進(jìn)去,然后存儲在ActivityClientRecord中伞剑。在Activity進(jìn)行relaunch的時(shí)候就會(huì)傳入之前的lastNonConfigurationInstances斑唬,這樣ViewModelStore是上一個(gè)的,自然ViewModel也是上一個(gè)的黎泣,完成了數(shù)據(jù)的保存
當(dāng)Activity正常銷毀時(shí)恕刘,則會(huì)通過clear方法清除所有的ViewModel