一党巾、介紹
ViewModel類是被設(shè)計用來以可感知生命周期的方式存儲和管理 UI 相關(guān)數(shù)據(jù),ViewModel中數(shù)據(jù)會一直存活即使 activity configuration發(fā)生變化榛鼎,比如橫豎屏切換的時候伐蒂。
以上是官網(wǎng)的話鸥昏,就不翻譯了沒啥意思存和,英語也不好。還是以我的大白話說說吧蜓席。
先來看 ViewModel 可以解決那些痛點。
1课锌、數(shù)據(jù)持久化
我們知道在屏幕旋轉(zhuǎn)的 時候 會經(jīng)歷 activity 的銷毀與重新創(chuàng)建厨内,這里就涉及到數(shù)據(jù)保存的問題,顯然重新請求或加載數(shù)據(jù)是不友好的渺贤。在 ViewModel 出現(xiàn)之前我們可以用 activity 的onSaveInstanceState()機制保存和恢復(fù)數(shù)據(jù)雏胃,但缺點很明顯,onSaveInstanceState只適合保存少量的可以被序列化志鞍、反序列化的數(shù)據(jù)瞭亮,假如我們需要保存是一個比較大的 bitmap list 固棚,這種機制明顯不合適。
由于 ViewModel 的特殊設(shè)計此洲,可以解決此痛點。
先來看下 ViewModel 生命周期圖:
由圖可知娶桦,ViewModel 生命周期是貫穿整個 activity 生命周期,包括 Activity 因旋轉(zhuǎn)造成的重創(chuàng)建衷畦,直到 Activity 真正意義上銷毀后才會結(jié)束。既然如此知牌,用來存放數(shù)據(jù)再好不過了祈争。
2、異步回調(diào)問題
通常我們 app 需要頻繁異步請求數(shù)據(jù)送爸,比如調(diào)接口請求服務(wù)器數(shù)據(jù)铛嘱。當(dāng)然這些請求的回調(diào)都是相當(dāng)耗時的,之前我們在 Activity 或 fragment里接收這些回調(diào)袭厂。所以不得不考慮潛在的內(nèi)存泄漏情況墨吓,比如 Activity 被銷毀后接口請求才返回。處理這些問題纹磺,會給我們增添好多復(fù)雜的工作帖烘。
但現(xiàn)在我們利用 ViewModel 處理數(shù)據(jù)回調(diào),可以完美的解決此痛點橄杨。
3秘症、分擔(dān) UI controller負(fù)擔(dān)
從最早的 MVC 到目前流行的 MVP照卦、MVVM,目的無非是 明確職責(zé)乡摹,分離 UI controller 負(fù)擔(dān)役耕。
UI controller 比如 Activity 、Fragment 是設(shè)計用來渲染展示數(shù)據(jù)聪廉、響應(yīng)用戶行為瞬痘、處理系統(tǒng)的某些交互。如果再要求他去負(fù)責(zé)加載網(wǎng)絡(luò)或數(shù)據(jù)庫數(shù)據(jù)板熊,會讓其顯得臃腫和難以管理框全。所以為了簡潔、清爽干签、絲滑津辩,我們可以分離出數(shù)據(jù)操作的職責(zé)給 ViewModel。
4容劳、Fragments 間共享數(shù)據(jù)
(可以先看下后面的用法介紹)
比如在一個 Activity 里有多個fragment喘沿,這fragment 之間需要做某些交互。我之前的做法是接口回調(diào)鸭蛙,需要統(tǒng)一在 Activity 里管理摹恨,并且不可避免的 fragment 之間還得互相持有對方的引用。仔細想想就知道這是很翔的一件事晒哄,耦合度高不說寝凌,還需要大量的容錯判斷(比如對方的 fragment 是否還活著)较木。
那么用 ViewModel 是怎么樣的呢(官網(wǎng)例子):
(activity 與其內(nèi)部的 fragment 可以共用一個ViewModel)
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// Update the UI.
});
}
}
仔細體會下這樣的好處會發(fā)現(xiàn):
1、Activity 不需要做任何事致开,甚至不知道這次交互双戳,完美解耦。
2峭竣、Fragment 只需要 與ViewModel交互皆撩,不需要知道對方 Fragment 的狀態(tài)甚至是否存在毅访,更不需要持有其引用。所有當(dāng)對方 Fragment 銷毀時蟆融,不影響本身任何工作型酥。
3弥喉、Fragment 生命周期互不影響由境,甚至 fragment 替換成其他的 也不影響這個系統(tǒng)的運作虏杰。
二、用法簡介
ViewModel一般配合 LiveData 使用瘸彤,LiveData可以參考我另一篇文章质况。
首先结榄,獲取 ViewModel 實例潭陪,通過提供的類ViewModelProviders:
MyViewModel model = ViewModelProviders.of(activity).get(MyViewModel.class);
或
MyViewModel model = ViewModelProviders.of(fragment).get(MyViewModel.class);
或帶有 Factory 的
MyViewModel model = ViewModelProviders.of(activity老厌,factory).get(MyViewModel.class);
VM 內(nèi)部操作:
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.
}
}
然后枝秤,可在 activity 觀察數(shù)據(jù)變化:
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 生命周期開始的時刻著手分析薇溃,那么什么時候開始的呢沐序?廢話策幼,當(dāng)然是從我們實例化它開始特姐,那么我們什么時候?qū)嵗厥虻倬W(wǎng)的原話是:
You usually request a ViewModel the first time the system calls an activity object's onCreate() method.
沒錯滤钱,我們一般在 onCreate 里初始化件缸。
ViewModel 的出生:
實例化的代碼很簡單,我們慢慢剖析争剿,做了哪些事情
ViewModelProviders.of(activity蚕苇,factory).get(MyViewModel.class)
1涩笤、 首先是ViewModelProviders 的 of 方法:
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
initializeFactoryIfNeeded(checkApplication(activity));
return new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory);
}
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@NonNull Factory factory) {
checkApplication(activity);
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
參數(shù)有 activity 與 fragment 的我就只貼 activity 的了 蹬碧,重點看這里引出了一個 Factory恩沽,不帶Factory的方法只是通過initializeFactoryIfNeeded初始化了一個sDefaultFactory(Factory的實現(xiàn)類):
/**
* Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
*/
public interface Factory {
/**
* Creates a new instance of the given {@code Class}.
* <p>
*
* @param modelClass a {@code Class} whose instance is requested
* @param <T> The type parameter for the ViewModel.
* @return a newly created ViewModel
*/
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
只有一個 create 方法罗心,用腳指頭想想也知道肯定是用來初始化viewmodel的渤闷。先放著里等會用到再說。繼續(xù)看 of 方法:
return new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory)
出現(xiàn)了兩個新的類ViewModelProvider與ViewModelStores爷贫,先看ViewModelProvider:
public class ViewModelProvider {
private static final String DEFAULT_KEY =
"android.arch.lifecycle.ViewModelProvider.DefaultKey";
private final Factory mFactory;
private final ViewModelStore mViewModelStore;
……
@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;
}
}
多余的都省略了卷员,此類很簡單 就是維護了 一個mFactory毕骡,一個新出現(xiàn)的類ViewModelStore(待會會講),并提供了用mFactory和ViewModelStore生成 ViewModel 的 get 方法窿撬。哇劈伴,這里就已經(jīng)看到 ViewModel 最終實例化的地方了握爷,但是別著急還有好多東西呢追城。
再來看
ViewModelStores.of(activity)
干了啥座柱。先看ViewModelStores:
/**
* Factory methods for {@link ViewModelStore} class.
*/
@SuppressWarnings("WeakerAccess")
public class ViewModelStores {
private ViewModelStores() {
}
/**
* Returns the {@link ViewModelStore} of the given activity.
*
* @param activity an activity whose {@code ViewModelStore} is requested
* @return a {@code ViewModelStore}
*/
@MainThread
public static ViewModelStore of(@NonNull FragmentActivity activity) {
return holderFragmentFor(activity).getViewModelStore();
}
/**
* Returns the {@link ViewModelStore} of the given fragment.
*
* @param fragment a fragment whose {@code ViewModelStore} is requested
* @return a {@code ViewModelStore}
*/
@MainThread
public static ViewModelStore of(@NonNull Fragment fragment) {
return holderFragmentFor(fragment).getViewModelStore();
}
}
只有兩個 of 方法色洞,holderFragmentFor為 HolderFragment 初始化的靜態(tài)方法, 劇透一下HolderFragment 發(fā)揮了至關(guān)重要的作用景用,這里先不講其重要作用 只看getViewModelStore()
public class HolderFragment extends Fragment {
……
private ViewModelStore mViewModelStore = new ViewModelStore();
public ViewModelStore getViewModelStore() {
return mViewModelStore;
}
……
}
沒啥說的伞插,看ViewModelStore:
public class ViewModelStore {
private final HashMap<String, ViewModel> 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);
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
很明顯是一個用來存放 ViewModel 實例的類媚污,內(nèi)部維護了一個 HashMap 存放 ViewModel耗美,
并提供了 get航缀,put芥玉,clear方法。
至此ViewModelProviders of 做了哪些事情呢:
1赶袄、初始化了ViewModelProvider內(nèi)部維護了 用于創(chuàng)建 VM 的 Factory饿肺,和用戶存放 VM 的ViewModelStore敬辣;
2购岗、初始化了 用來生成 ViewModel 的 Factory(默認(rèn)為DefaultFactory)喊积;
3乾吻、通過ViewModelStores的靜態(tài)方法實例化了 HolderFragment绎签,并實例化了ViewModelStore
2诡必、然后是ViewModelProvider的 get 方法:
上面已經(jīng)貼過了,再貼一下吧:
ViewModelProviders.of(activity蟋字,factory).get(MyViewModel.class)鹊奖;
@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;
}
邏輯不復(fù)雜,先看ViewModelStore是不是已經(jīng)存了唱捣,沒有的話就通過 factory 實例化, 并存到 ViewModelStore 中垫竞。
ViewModel 的死亡:
再來看 ViewModel 是如何結(jié)束其一生的。
public abstract class ViewModel {
/**
* This method will be called when this ViewModel is no longer used and will be destroyed.
* <p>
* It is useful when ViewModel observes some data and you need to clear this subscription to
* prevent a leak of this ViewModel.
*/
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
}
Vm 只有一個onCleared方法徐裸,那么他是在何時調(diào)用的呢
這里就要介紹之前提到的 HolderFragment了啸盏,
public class HolderFragment extends Fragment {
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();
}
public ViewModelStore getViewModelStore() {
return mViewModelStore;
}
static class HolderFragmentManager {
private Map<Activity, HolderFragment> mNotCommittedActivityHolders = new HashMap<>();
private Map<Fragment, HolderFragment> mNotCommittedFragmentHolders = new HashMap<>();
……
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;
}
……
}
Vm 創(chuàng)建的時候提到過 實例化了一個 HolderFragment 。并且實例化的時候通過上面createHolderFragment 方法將其fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
我們知道commit 之后 fragment 將會擁有靈魂潜圃,獲得生命周期谭期。再看其onDestroy方法里
調(diào)用了 mViewModelStore.clear();
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
至此隧出,Google 充分利用了 fragment 生命周期的特性胀瞪,使得 Vm 完成了 onCleared圆雁。
(這里我不得不說一下幔摸,Google 的 lifecycle 與 ViewModel 全都是利用 fragment 的一些特性去玩這些生命周期既忆,這么喜歡用 fragment是為什么我現(xiàn)在還沒完全領(lǐng)悟)
那么問題來了 為什么橫豎屏切換 ViewModel 不會 onCleared?
看 HolderFragment 的構(gòu)造方法里有個
setRetainInstance(true);
所以一切都了然了患雇,(如果你沒有了然就百度一下這個方法是干嘛的)
Google 真是充分的利用 fragment 特點跃脊。
四、注意
官網(wǎng)用一個大大的紅色感嘆號表明:
Caution: A ViewModel
must never reference a view, Lifecycle
, or any class that may hold a reference to the activity context.
由于 ViewModel 生命周期可能長與 activity 生命周期苛吱,所以為了避免內(nèi)存泄漏 Google 禁止在 ViewModel 中持有 Context 或 activity 或 view 的引用酪术。
這個讓我糾結(jié)了好久,后來發(fā)現(xiàn) 有一個 AndroidViewModel 類翠储,內(nèi)部維護了一個 ApplicationContext 實在不行也可以用著個
public class AndroidViewModel extends ViewModel {
@SuppressLint("StaticFieldLeak")
private Application mApplication;
public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}
/**
* Return the application.
*/
@NonNull
public <T extends Application> T getApplication() {
//noinspection unchecked
return (T) mApplication;
}
}