天天看點

android view系統解析,Android源碼解析-ViewModel

前言

最近一個月主要實作了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的調用,發現這裡的實作應該跟生命周期處理有關。

android view系統解析,Android源碼解析-ViewModel

google搜尋了一下Activity的onRetainNonConfigurationInstance的作用:大部分同學知道Activity因為configuration變化銷毀和重建時會調用onSaveInstanceState和onRestoreInstanceState。與此同時,Activity其實還會回調onRetainNonConfigurationInstance和getLastNonConfigurationInstance方法。

onRetainNonConfigurationInstance和onSaveInstanceState作用相同,用來儲存UI相關變量,當Activity意外銷毀時,Activity的ViewModelStore對象就是在這裡進行了儲存。

那什麼時機進行的恢複呢?

當Activity的onCreate調用時,會調用getLastNonConfigurationInstance,擷取之前儲存的ViewModelStore,如果ViewModelStore不為空,就進行指派。這裡進行了ViewModelStore的恢複。

android view系統解析,Android源碼解析-ViewModel

小結

這裡我們又學到了Activity的兩個跟生命周期相關的函數調用:onRetainNonConfigurationInstance和getLastNonConfigurationInstance。

Activity實作了ViewModelStoreOwner接口,建立了ViewModelStore對象。

當Activity意外銷毀時,onRetainNonConfigurationInstance函數被回調,在此函數中對ViewModelStore對象進行了儲存。

當Activity重建時,onCreate方法中會先擷取getLastNonConfigurationInstance,如果其中的ViewModelStore對象不為空,就直接引用,不再重新建立ViewModelStore對象了。

總結

三個問題分析完畢,相信大家已經對ViewModel的實作原理比較熟悉了。建議大家以後學習源碼時,也帶着問題去分析思考,事半功倍。