本文是深刻理解「Android Architecture Components」系列文章第四篇 源碼基於 AAC 1.1.1 版本html
【AAC 系列一】Android 應用架構新時代來臨!java
【AAC 系列二】深刻理解架構組件的基石:Lifecycleandroid
這個連接不要點,有毒,千萬不要github
在上一篇 LiveData
原理分析一文中,咱們提到了 ViewModel
,它跟 LiveData 配合可以把價值發揮到最大。緩存
這一篇,咱們就來深刻淺出一下 ViewModel ,來說講 ViewModel 的使用方式、生命週期、以及它的實現原理。架構
在深刻講解 ViewModel 以前,先來簡單瞭解下 ViewModel:app
The
ViewModel
class is designed to store and manage UI-related data in a lifecycle conscious way. TheViewModel
class allows data to survive configuration changes such as screen rotations.async
ViewModel 被設計來管理跟 UI 相關的數據, 而且可以感知生命週期;另外 ViewModel 可以在配置改變
的狀況下讓數據得以保留。ViewModel 重在以感知生命週期的方式
管理界面相關的數據。ide
咱們知道相似旋轉屏幕等配置項改變會致使咱們的 Activity 被銷燬並重建,此時 Activity 持有的數據就會跟隨着丟失,而ViewModel 則並不會被銷燬
,從而可以幫助咱們在這個過程當中保存數據,而不是在 Activity 重建後從新去獲取。而且 ViewModel 可以讓咱們沒必要去擔憂潛在的內存泄露問題
,同時 ViewModel 相比於用onSaveInstanceState()
方法更有優點,好比存儲相對大的數據,而且不須要序列化以及反序列化。
總之 ViewModel,優勢多多,接下去咱們介紹下 ViewModel 的基本使用。
ViewModel 的使用也很是簡單,Android 提供了一個 ViewModel 類讓咱們去繼承,而且提供了 ViewModelProviders
來幫助咱們實例化 ViewModel。
搬運一個官網例子以下:
a):先添加一下依賴:
def lifecycle_version = "1.1.1"
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
複製代碼
b):自定義一個MyViewModel
繼承自ViewModel
,而且包含了一個 LiveData
:
public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<User>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
複製代碼
c):在 Activity 中藉助 ViewModelProviders
得到 ViewModel 的實例,並藉助 LiveData 訂閱 users 的變化通知:
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same MyViewModel instance created by the first activity.
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// update UI
});
}
}
複製代碼
就這樣簡單的步驟,咱們就使用上了 ViewModel,即使 MyActivity 從新建立,MyActivity 拿到的 MyViewModel 都會是一個實例。
那麼問題來了, ViewModel 的生命週期究竟是怎麼樣的呢?
它背後蘊藏什麼原理呢?我們接下來看看。
咱們在前面提到過,ViewModel 並不會由於 Activity 的配置改變銷燬而一塊兒銷燬,那麼 ViewModel 的生命週期究竟是怎麼樣的呢?
看一張官網給的圖:
能夠看到 ViewModel 在 Activity 的重建時依然存活。
Why
?
回顧下咱們以前使用 ViewModel 的代碼:
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
複製代碼
這裏先從 ViewModelProviders.of
方法入手看看:
//ViewModelProviders
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
//傳入了 null
return of(activity, null);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
//檢查合法性
Application application = checkApplication(checkActivity(fragment));
if (factory == null) {
//走到這裏,返回了 AndroidViewModelFactory 的單例
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
複製代碼
ViewModelProviders.of()
方法幫咱們在默認狀況下構建了一個 AndroidViewModelFactory
工廠類,來幫助建立 ViewModel,而且返回了一個在當前 Activity 生命週期內的 ViewModelProvider
。
看一下 AndroidViewModelFactory
:
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 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) {
//...
}
}
return super.create(modelClass);
}
}
複製代碼
AndroidViewModelFactory 其實就是一個經過反射
方法來構建 ViewModel 的工廠類,且是個單例。
看下來發現咱們 ViewModel 的 class 是傳給了 ViewModelProvider.get()
方法。
來看看 get 方法:
//ViewModelProvider
@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");
}
//每一個類都有一個惟一的 key
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//先從 store 中獲取
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;
}
複製代碼
解釋下代碼,發現它爲每一個 ViewModel 的 class 都構建了一個惟一的 key
, 並經過這個 key 嘗試去一個叫作 ViewModelStore
的類獲取 ViewModel 的實例,若是爲null ,那麼會經過 Factory 去建立,並把新的實例存入到 ViewModelStore。
主要的流程彷佛就跟咱們平時作緩存同樣,好像沒什麼特別的東西,沒有看到一絲跟生命週期相關的處理的代碼,這是怎麼回事?
再仔細思考一下,get 方法會優先從這個 ViewModelStore 中去拿,那麼理論上只要保證 ViewModelStore 這個類在配置變化的過程當中沒有被銷燬,那麼就能夠保證咱們建立的 ViewModel 不會被銷燬,咱們確定漏掉了關於 ViewModelStore 的重要東西。
回過去再仔細看一下,ViewModelProvider 的構建好像並不簡單:
new ViewModelProvider(ViewModelStores.of(fragment), factory);
複製代碼
這裏傳入了一個 經過 ViewModelStores 類建立的 ViewModelStore,而且傳入了 fragment,必定有蹊蹺。
//ViewModelStores
public static ViewModelStore of(@NonNull Fragment fragment) {
if (fragment instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) fragment).getViewModelStore();
}
return holderFragmentFor(fragment).getViewModelStore();
}
複製代碼
這裏又多出來了幾個類 ViewModelStoreOwner、HolderFragment ,讓咱們來追查一下。
public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
}
複製代碼
ViewModelStoreOwner 跟 LifecycleOwner 相似,只是個接口定義,重點來看看holderFragmentFor(fragment)
方法返回的 HolderFragment
。
//HolderFragment
public static HolderFragment holderFragmentFor(Fragment fragment) {
return sHolderFragmentManager.holderFragmentFor(fragment);
}
複製代碼
方法又走到了 HolderFragmentManager 類,怎麼又多了個 HolderFragmentManager ,神煩啊。
static class HolderFragmentManager {
private Map<Activity, HolderFragment> mNotCommittedActivityHolders = new HashMap<>();
private Map<Fragment, HolderFragment> mNotCommittedFragmentHolders = new HashMap<>();
private ActivityLifecycleCallbacks mActivityCallbacks =
new EmptyActivityLifecycleCallbacks() {
@Override
public void onActivityDestroyed(Activity activity) {
HolderFragment fragment = mNotCommittedActivityHolders.remove(activity);
if (fragment != null) {
Log.e(LOG_TAG, "Failed to save a ViewModel for " + activity);
}
}
};
private boolean mActivityCallbacksIsAdded = false;
private FragmentLifecycleCallbacks mParentDestroyedCallback =
new FragmentLifecycleCallbacks() {
@Override
public void onFragmentDestroyed(FragmentManager fm, Fragment parentFragment) {
super.onFragmentDestroyed(fm, parentFragment);
HolderFragment fragment = mNotCommittedFragmentHolders.remove(
parentFragment);
if (fragment != null) {
Log.e(LOG_TAG, "Failed to save a ViewModel for " + parentFragment);
}
}
};
void holderFragmentCreated(Fragment holderFragment) {
Fragment parentFragment = holderFragment.getParentFragment();
if (parentFragment != null) {
mNotCommittedFragmentHolders.remove(parentFragment);
parentFragment.getFragmentManager().unregisterFragmentLifecycleCallbacks(
mParentDestroyedCallback);
} else {
mNotCommittedActivityHolders.remove(holderFragment.getActivity());
}
}
private static HolderFragment findHolderFragment(FragmentManager manager) {
if (manager.isDestroyed()) {
throw new IllegalStateException("Can't access ViewModels from onDestroy");
}
Fragment fragmentByTag = manager.findFragmentByTag(HOLDER_TAG);
if (fragmentByTag != null && !(fragmentByTag instanceof HolderFragment)) {
throw new IllegalStateException("Unexpected "
+ "fragment instance was returned by HOLDER_TAG");
}
return (HolderFragment) fragmentByTag;
}
private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
HolderFragment holder = new HolderFragment();
fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
return holder;
}
HolderFragment holderFragmentFor(FragmentActivity activity) {
FragmentManager fm = activity.getSupportFragmentManager();
HolderFragment holder = findHolderFragment(fm);
if (holder != null) {
return holder;
}
holder = mNotCommittedActivityHolders.get(activity);
if (holder != null) {
return holder;
}
if (!mActivityCallbacksIsAdded) {
mActivityCallbacksIsAdded = true;
activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
}
holder = createHolderFragment(fm);
mNotCommittedActivityHolders.put(activity, holder);
return holder;
}
HolderFragment holderFragmentFor(Fragment parentFragment) {
FragmentManager fm = parentFragment.getChildFragmentManager();
HolderFragment holder = findHolderFragment(fm);
if (holder != null) {
return holder;
}
holder = mNotCommittedFragmentHolders.get(parentFragment);
if (holder != null) {
return holder;
}
parentFragment.getFragmentManager()
.registerFragmentLifecycleCallbacks(mParentDestroyedCallback, false);
holder = createHolderFragment(fm);
mNotCommittedFragmentHolders.put(parentFragment, holder);
return holder;
}
}
複製代碼
從源碼中咱們能夠獲知 HolderFragmentManager
主要作這幾件事:
在咱們想要獲取 ViewModel 實例的時候,會先構建一個 HolderFragment
,將它添加咱們的宿主(Activity/Fragment)中
,並將它緩存起來;
同時經過註冊回調來監聽宿主的生命週期,Activity 對應 ActivityLifecycleCallbacks
,Fragment 對應 FragmentLifecycleCallbacks
,在宿主銷燬的時候清理緩存;
類如其名,HolderFragmentManager
負責管理 HolderFragment,看到它注入了 HolderFragment,接下去看看 HolderFragment。
HolderFragment
源碼精簡以下:
public class HolderFragment extends Fragment implements ViewModelStoreOwner {
private static final String LOG_TAG = "ViewModelStores";
private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();
/** * @hide */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static final String HOLDER_TAG =
"android.arch.lifecycle.state.StateProviderHolderFragment";
private ViewModelStore mViewModelStore = new ViewModelStore();
//看這裏看這裏看這裏
public HolderFragment() {
setRetainInstance(true);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sHolderFragmentManager.holderFragmentCreated(this);
}
...
@Override
public void onDestroy() {
super.onDestroy();
mViewModelStore.clear();
}
}
複製代碼
HolderFragment 內部持有了一個ViewModelStore
,而且實現了咱們以前提到的 ViewModelStoreOwner
接口,而且最爲主要的是這段代碼:
public HolderFragment() {
setRetainInstance(true);
}
複製代碼
Fragment.setRetainInstance(true)
方法能夠實現的效果爲,在 Activity 配置改變後依然保存。
到這裏 ViewModel 實現的原理就清晰了:經過注入一個retainInstance 爲 true 的 HolderFragment ,利用 Fragment 的特性來保證在 Activity 配置改變後依然可以存活一下,而且保證了 HolderFragment 內部的 ViewModelStore 的存活,最終保證了 ViewModelStore 內部儲存的 ViewModel 緩存存活,從而實現了 ViewModel 的生命週期這個特色功能。(又是 Fragment!)
ViewModel 重點類類圖:
ViewModel原理實現序列圖:
重點類講解:
ViewModel
,抽象類,用來負責準備和管理 Activity/Fragment 的數據,而且還能處理 Activity/Fragment 跟外界的通訊,一般還存放業務邏輯,相似 Presenter;ViewModel 一般會暴露 LiveData 給 Activity/Fragment;而且 Activity 配置改變並不會致使 ViewModel 回收;AndroidViewModel
,一個會持有 Application
的 ViewModel;ViewModelStore
,負責存儲 ViewModel 的類,而且還負責在 ViewModel 被清除以前通知它,也即調用 ViewModel.onCleared()
;ViewModelStoreOwner
, 是抽象 「ViewModelStore 的擁有者」 的接口定義,相似 LifecycleOwner 的角色,實現了它的有 HolderFragment、FragmentActivity;HolderFragment
,一個 retainInstance
屬性爲true
並實現了 ViewModelStoreOwner
的 Fragment,用來保存 ViewModelStore
,並保證它在配置修改時不被銷燬;HolderFragmentManager
,負責建立、注入、緩存等管理 HolderFragment 的工做;ViewModel 原理總結:
經過注入一個 retainInstance
爲true
的 HolderFragment ,利用 Fragment 的特性來保證在 Activity 配置改變後依然可以存活下來,而且保證了 HolderFragment 內部的 ViewModelStore 的存活,最終保證了 ViewModelStore 內部儲存的 ViewModel 緩存存活,從而實現 ViewModel 在 Activity 配置改變的狀況下不銷燬的功能。
ViewModel 的使用注意事項:
不要持有 Activity
:ViewModel 不會由於 Activity 配置改變而被銷燬,因此絕對不要持有那些跟 Activity 相關的類,好比Activity 裏的某個 View,讓 ViewModel 持有 Activity 會致使內存泄露,還要注意的是連 Lifecycle 也不行;不能訪問 UI
:ViewModel 應該只負責管理數據,不能去訪問 UI,更不能持有它;ViewModel 利用 Fragment 的特性,提供給咱們一個方式在特定的生命週期內去管理跟 UI 相關的數據;可以幫助咱們把數據管理的邏輯從 Activity/Fragment 中剝離開。
實際上 ViewModel 不只能夠管理數據,並且還能夠存放業務邏輯處理的代碼,另外還可以方便 Activity 中的不一樣Fragment 之間互相通訊,這個解決了以往咱們 Fragment 之間通訊的一個大問題。
深刻了解完 Lifecycle、LiveData、ViewModel 以後,能夠發現它們確實很是強大,可以切實的幫助咱們解決實際開發過程當中遇到的問題。
強烈推薦你們趕忙上車體驗,再晚就買不到票了啊。