剛?cè)肟蛹軜?gòu)組件沒(méi)多久,發(fā)現(xiàn)很多基礎(chǔ)性的東西理解起來(lái)是沒(méi)什么問(wèn)題的,但是一到具體使用就各種問(wèn)題,相關(guān)實(shí)踐文章也比較少,更多的只能靠自己解決 = =。今天無(wú)意間了解了AndroidViewModel的一個(gè)使用場(chǎng)景和實(shí)現(xiàn)原理,特地記錄下來(lái)。
前言
一開(kāi)始,跟著官方文檔,ViewModel我們是這樣實(shí)現(xiàn)的:
public class MyViewModel entends ViewModel {
//...
}
然后是創(chuàng)建實(shí)例對(duì)象:
MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
可以注意到,我們定義的ViewModel是沒(méi)有構(gòu)造函數(shù)的,也就是說(shuō)如果遇到比較復(fù)雜情況下(需要在創(chuàng)建ViewModel時(shí)賦一些初始值),應(yīng)該怎么辦呢?總不能自己加一個(gè)構(gòu)造函數(shù),然后new出來(lái)吧,或者通過(guò)set方法一個(gè)一個(gè)加也行,但是這樣就有點(diǎn)low了。于是,我發(fā)現(xiàn)了一個(gè)類(lèi)AndroidViewModel
AndroidViewModel
AndroidViewModel這個(gè)類(lèi)的定義很簡(jiǎn)單,它繼承自ViewModel,然后添加了一個(gè)application私有屬性:
public class AndroidViewModel extends ViewModel {
@SuppressLint("StaticFieldLeak")
private Application mApplication;
public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}
/**
* Return the application.
*/
@SuppressWarnings("TypeParameterUnusedInFormals")
@NonNull
public <T extends Application> T getApplication() {
//noinspection unchecked
return (T) mApplication;
}
}
上面提到,很多時(shí)候我們需要在初始化ViewModel時(shí)需要傳遞一些參數(shù),例如Activity中的一些參數(shù)等,但是根據(jù)ViewModel的生命周期我們知道,ViewModel中是不能傳入Activity等實(shí)例對(duì)象的,因?yàn)樵?code>ViewModel存活的過(guò)程中,Activity是有可能會(huì)被銷(xiāo)毀的。因此,Google爸爸推薦我們傳入application。
下面是AndroidViewModel的一個(gè)使用示例,其實(shí)我們只需要將ViewModel改為繼承AndroidViewModel就可以了:
public class MyViewModel extends AndroidViewModel {
public MyViewModel(@NonNull Application application) {
super(application);
}
//...
}
使用的話(huà)還是一樣的:
MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
AndroidViewModel實(shí)現(xiàn)原理
到了這里,疑問(wèn)就來(lái)了,我創(chuàng)建實(shí)例的時(shí)候并依然沒(méi)有傳遞任何參數(shù),那么那個(gè)application是怎么來(lái)的呢,下面我們來(lái)看下源碼中是怎么實(shí)現(xiàn)的:
//ViewModelProviders.java
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
ViewModelProviders中有很多of的重載方法,這里暫時(shí)只需要關(guān)注上面這兩個(gè)即可,可以看到,application是從我們傳入的activity中獲取到的,并且此時(shí)還創(chuàng)建了一個(gè)factory實(shí)例對(duì)象(劃重點(diǎn),后面會(huì)用到)。默認(rèn)情況下(傳遞的factory為null),那么會(huì)自動(dòng)創(chuàng)建一個(gè)AndroidViewModelFactory ,AndroidViewModelFactory 是ViewModelProvider中的一個(gè)靜態(tài)內(nèi)部類(lèi)。
接著我們來(lái)看get方法:
//ViewModelProvider.java
@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.
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
可以看到,最終生成的ViewModel是通過(guò)(mFactory).create方法生成的,這個(gè)factory就是上面所提到的,然后在去看他的create方法實(shí)現(xiàn):
//ViewModelProvider.java
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);
}
}
轉(zhuǎn)到AndroidViewModelFactory的實(shí)現(xiàn),可以看到默認(rèn)生成的factory的create方法中使用反射調(diào)用了ViewModel的構(gòu)造函數(shù),至此,整個(gè)過(guò)程就還原了。
自定義ViewModel構(gòu)造函數(shù)
了解了AndroidViewModel的整個(gè)實(shí)現(xiàn)流程后,自定義流程就好辦了,思路是一致的,需要借助一個(gè)Factory來(lái)實(shí)現(xiàn):
public class MyViewModel2 extends ViewModel {
private String name;
public MyViewModel2(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//...
}
public class MyViewModelFactory implements ViewModelProvider.Factory {
@SuppressWarnings("unchecked")
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (modelClass.isAssignableFrom(MyViewModel2.class)) {
return (T) new MyViewModel2("Tom");
}
throw new RuntimeException("unknown class :" + modelClass.getName());
}
}
MyViewModel2 myViewModel2 = ViewModelProviders.of(this, new MyViewModelFactory()).get(MyViewModel2.class);
Log.d("TAG", "onCreate: name = "+myViewModel2.getName());
大功告成。
雖然這里只是一個(gè)簡(jiǎn)單的例子,但是既然思路已經(jīng)出來(lái)了,剩下的就是八仙過(guò)海各顯神通了。
還在踩坑階段,如有什么解釋不當(dāng)?shù)牡胤綒g迎指正。(▽)