前言
最近一個月主要實作了IM功能,其中UI架構使用了ViewModel和LiveData的存儲和通知機制,實作後代碼簡潔易于維護。
感慨于Android arch components控件強大同時,需要順帶分析一波其中源碼實作。今天先來分析一下ViewModel的源碼實作。
ViewModel簡介
ViewModel是通過關聯生命周期的方式來存儲和管理跟UI相關的資料。即使configuration發生變化,在ViewModel中存放的資料是不會被銷毀的。
沒使用ViewModel的時候,如果系統Configuration發生變化,我們的Activity會被銷毀重建,導緻Activity中的UI資料丢失。為了規避這個問題,我們隻能在onSaveInstanceState()将UI資料進行儲存,在onCreate方法中判斷savedInstanceState中是否有我們存儲的資料。
有了ViewModel之後,我們隻需要将資料存儲到ViewModel即可,ViewModel中的資料不會随着Activity的銷毀重建而消失。同時,如果不同的Fragment使用相同的Activity對象來擷取ViewModel,可以輕易的實作資料共享和通信。
使用ViewModel的例子:
// 自定義一個ViewModel,存儲一個字元串
public class TestViewModel extends ViewModel {
public String content;
@Override
protected void onCleared() {
// 資料清理工作
content = null;
}
}
// 在Activity中擷取并使用ViewModel
TestViewModel viewModel = ViewModelProviders.of(activity).get(TestViewModel.class);
Log.d("tag", viewModel.content);
三個問題
ViewModel是如何建立出來的?
為什麼不同的Fragment使用相同的Activity對象來擷取ViewModel,可以輕易的實作ViewModel共享?
ViewModel為什麼在Activity銷毀重建時不會被銷毀回收?
我個人習慣,看源碼時候總是要帶着問題去分析了解,這樣才會有所收獲。
源碼分析
ViewModel是如何建立出來的?
通過示例代碼,去掉鍊式調用後,我們可以看到ViewModel是通過如下兩步建立出來的:
// 1. 建立ViewModelProvider
ViewModelProvider viewModelProvider = ViewModelProviders.of(activity);
// 2. 通過反射擷取ViewModel
TestViewModel viewModel = viewModelProvider.get(TestViewModel.class);
建立ViewModelProvider
先看下第一步的源碼實作:
public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) {
if (factory == null) {
// 如果傳入的對象建立工廠類為null,則使用預設的AndroidViewModelFactory來建立對象
factory = AndroidViewModelFactory.getInstance(application);
}
// 建立一個ViewModelProvider
return new ViewModelProvider(ViewModelStores.of(activity), (Factory)factory);
}
源碼中,我們發現建立一個ViewModelProvider需要傳入兩個參數:ViewModelStore和Factory。我們先看下Factory的實作。
Factory
Factory顧名思義,定義了建立ViewModel的行為接口。裡面隻有一個create方法,用于子類自行決定如何實作一個ViewModel對象的建立。
public interface Factory {
T create(@NonNull Class modelClass);
}
同時,ViewModelProvider源碼内部也提供了兩個預設Factory實作:NewInstanceFactory和AndroidViewModelFactory。
// 直接反射Class對象的無參構造函數來建立ViewModel
public static class NewInstanceFactory implements Factory {
@Override
public T create(@NonNull Class modelClass) {
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);
}
}
}
// 1. 如果對象繼承自AndroidViewModel,發射調用帶Application參數的構造函數建立ViewModel對象;
// 2. 如果對象不繼承自AndroidViewModel,則直接調用父類,即調用Class的無參構造函數建立ViewModel對象。
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static AndroidViewModelFactory sInstance;
@NonNull
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
private Application mApplication;
public AndroidViewModelFactory(@NonNull Application application) {
mApplication = application;
}
@NonNull
@Override
public T create(@NonNull Class 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);
}
}
啟示:我們自定義的ViewModel對象如果構造函數中需要其他各種參數,我們隻需要建立一個自定義的Factory類,然後調用該class的有參構造函數進行建立即可。
ViewModelStore
ViewModelStore就是個HashMap,通過key來擷取ViewModel對象。
public class ViewModelStore {
private final HashMap mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.get(key);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
mMap.put(key, viewModel);
}
final ViewModel get(String key) {
return mMap.get(key);
}
}
ViewModelProvider
了解了Factory實作和ViewModelStore實作後,我們來看一下ViewModelProvider的get方法是如何建立ViewModel對象的。
public class ViewModelProvider {
private final Factory mFactory;
private final ViewModelStore mViewModelStore;
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
public T get(@NonNull Class modelClass) {
String canonicalName = modelClass.getCanonicalName();
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
@NonNull
@MainThread
public T get(@NonNull String key, @NonNull Class modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
}
ViewModleProvider的get方法實作也比較簡單,總結一下就是:
使用ViewModel Class的canonicalName作為ViewModel在ViewModelStore中的唯一辨別。
通過唯一辨別,先查詢一下ViewModelStore中是否有該ViewModel對象,如果有則直接傳回。
如果ViewModelStore中沒有該ViewModel對象,則通過Factory工廠類反射建立出ViewModel對象,存入ViewModelStore中,并傳回給調用者。
小結
至此,第一個問題ViewModel是如何建立出來的已經分析完畢。接下來,我們看第二個問題。
為什麼不同的Fragment使用相同的Activity對象來擷取ViewModel,可以輕易的實作ViewModel共享?
講道理,如果同學們仔細看了ViewModel的建立流程,這個問題自然迎刃而解。
因為不同的Fragment使用相同的Activity對象來擷取ViewModel,在建立ViewModel之前都會先從Activity提供的ViewModelStore中先查詢一遍是否已經存在該ViewModel對象。是以我們隻需要先在Activity中同樣調用一遍ViewModel的擷取代碼,即可讓ViewModel存在于ViewModelStore中,進而不同的Fragment可以共享一份ViewModel了。
ViewModel為什麼在Activity銷毀重建時不會被銷毀回收?
在看這個問題之前,我們回到ViewModelProviders.of()源碼中:
public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) {
if (factory == null) {
// 如果傳入的對象建立工廠類為null,則使用預設的AndroidViewModelFactory來建立對象
factory = AndroidViewModelFactory.getInstance(application);
}
// 建立一個ViewModelProvider
return new ViewModelProvider(ViewModelStores.of(activity), (Factory)factory);
}
ViewModelProvider擷取ViewModelStore是通過ViewModelStores.of(activity)實作的。我們先看一下這個ViewModelStores做了什麼操作。
ViewModelStores
看名字又像是一個封裝好的工廠類。看下源碼:
public static ViewModelStore of(@NonNull FragmentActivity activity) {
return activity instanceof ViewModelStoreOwner ? ((ViewModelStoreOwner)activity).getViewModelStore() : HolderFragment.holderFragmentFor(activity).getViewModelStore();
}
通過源碼可以發現,如果Activity是ViewModelStoreOwner的實作類,則直接通過activity擷取ViewModelStore,如果不是,則通過HolderFragment.holderFragmentFor(activity).getViewModelStore()來擷取。
通過檢視FragmentActivity源碼,發現其已經是實作了ViewModelStoreOwner接口。
public class FragmentActivity extends BaseFragmentActivityApi16 implements
ViewModelStoreOwner,
ActivityCompat.OnRequestPermissionsResultCallback,
ActivityCompat.RequestPermissionsRequestCodeValidator {
}
是以這裡我們已經不需要理會HolderFragment了,很多分析ViewModel源碼的文章都會花很多篇幅分析HolderFragment,我實在是搞不懂為什麼要這種操作。(初步懷疑是互相抄襲。。.)
雖然FragmentActivity實作了ViewModelStoreOwner接口,能夠提供ViewModelStore,但是ViewModelStore是如何跟Activity生命周期關聯起來的呢?
FragmentActivity中ViewModelStore的生命周期處理
搜尋了一下FragmentActivity中關于ViewModelStore的調用,發現這裡的實作應該跟生命周期處理有關。

google搜尋了一下Activity的onRetainNonConfigurationInstance的作用:大部分同學知道Activity因為configuration變化銷毀和重建時會調用onSaveInstanceState和onRestoreInstanceState。與此同時,Activity其實還會回調onRetainNonConfigurationInstance和getLastNonConfigurationInstance方法。
onRetainNonConfigurationInstance和onSaveInstanceState作用相同,用來儲存UI相關變量,當Activity意外銷毀時,Activity的ViewModelStore對象就是在這裡進行了儲存。
那什麼時機進行的恢複呢?
當Activity的onCreate調用時,會調用getLastNonConfigurationInstance,擷取之前儲存的ViewModelStore,如果ViewModelStore不為空,就進行指派。這裡進行了ViewModelStore的恢複。
小結
這裡我們又學到了Activity的兩個跟生命周期相關的函數調用:onRetainNonConfigurationInstance和getLastNonConfigurationInstance。
Activity實作了ViewModelStoreOwner接口,建立了ViewModelStore對象。
當Activity意外銷毀時,onRetainNonConfigurationInstance函數被回調,在此函數中對ViewModelStore對象進行了儲存。
當Activity重建時,onCreate方法中會先擷取getLastNonConfigurationInstance,如果其中的ViewModelStore對象不為空,就直接引用,不再重新建立ViewModelStore對象了。
總結
三個問題分析完畢,相信大家已經對ViewModel的實作原理比較熟悉了。建議大家以後學習源碼時,也帶着問題去分析思考,事半功倍。