前言
上篇我們分析了Livedata的使用及原理,相信我們已經(jīng)學(xué)會(huì)了使用Livedata來存儲(chǔ)數(shù)據(jù),并在觀察者組件中實(shí)現(xiàn)回調(diào)方法,來動(dòng)態(tài)更新UI數(shù)據(jù)。這里奉上(雙膝已經(jīng)跪爛了...)上兩篇的地址:
Android架構(gòu)組件(一):Lifecycle
Android架構(gòu)組件(二):LiveData
方便大家進(jìn)行查閱和回顧。
那么,接下來我們要學(xué)習(xí)我們的第三個(gè)架構(gòu)組件——Viewmodel,我們從字面上理解,它肯定和view,model有關(guān)聯(lián),它是負(fù)責(zé)準(zhǔn)備和管理UI組件(activity/fragment)相關(guān)的數(shù)據(jù)類,也就是說Viewmodel是用來管理UI相關(guān)數(shù)據(jù)的,同時(shí)Viewmodel還可以負(fù)責(zé)UI間組件的通訊。
Viewmodel是什么?
我們已經(jīng)知道,Viewmodel有以下兩點(diǎn)作用:
- 用來管理數(shù)據(jù)(model)和UI組件(view)的數(shù)據(jù)類
- 負(fù)責(zé)UI組件之間的通訊
-
管理數(shù)據(jù)(model)和UI組件(view)的數(shù)據(jù)
我們先來看一下它的基本使用:
public class MyViewModel extends ViewModel {
//如果不熟悉Livedata用法可以閱讀上一篇博客
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<Users>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// 異步調(diào)用獲取用戶列表
...
users.setValue(data);
}
}
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// 更新 UI
});
}
}
用法很簡單,
我們?cè)?strong>viewmodel中定義一個(gè)livedata的集合,通過網(wǎng)絡(luò)獲取數(shù)據(jù)后,調(diào)用setValue方法通知觀察者(UI)在活躍狀態(tài)下時(shí)更新數(shù)據(jù)。
在activity中,我們初始化viewmodel拿到livedata并在他的onchanged()方法里做UI相關(guān)操作。
這里我們發(fā)現(xiàn)viewmodel做了一個(gè)中間人的角色,它管理著model與view之間相互關(guān)聯(lián)的數(shù)據(jù),這樣我們就可以把數(shù)據(jù)相關(guān)(model)操作放到viewmodel中,把UI操作放到view中,完全由viewmodel管理,使model與view層完全解耦。
-
負(fù)責(zé)UI間組件之間的通訊
一個(gè)activity中的多個(gè)Fragment互相間通訊時(shí)很常見的需求,我們可以使用activity中的viewmodel來實(shí)現(xiàn)fragment之間數(shù)據(jù)的共享。
下面這個(gè)例子也很簡單:
//我們定義viewmodel并設(shè)置set,get方法
public class CommunicateViewModel extends ViewModel {
private MutableLiveData<String> mNameLiveData;
public LiveData<String> getName(){
if (mNameLiveData == null) {
mNameLiveData = new MutableLiveData<>();
}
return mNameLiveData;
}
public void setName(String name){
if (mNameLiveData != null) {
mNameLiveData.setValue(name);
}
}
}
//我們通過fragment1設(shè)置name的值
public class FragmentOne extends Fragment {
private CommunicateViewModel mCommunicateViewModel;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCommunicateViewModel = ViewModelProviders.of(getActivity()).get(CommunicateViewModel.class);
}
@OnClick(R.id.btn_set_name)
void onViewClicked(View v){
switch (v.getId()){
case R.id.btn_set_name:
mCommunicateViewModel.setName("Jane");
break;
}
}
}
//在fragment2中我們通過同一個(gè)viewmodel拿到livedata并更新UI
public class FragmentTwo extends Fragment {
private CommunicateViewModel mCommunicateViewModel;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCommunicateViewModel = ViewModelProviders.of(getActivity()).get(CommunicateViewModel.class);
mCommunicateViewModel.getName().observe(this, name -> mTvName.setText(name));
}
}
上述代碼我們知道了,兩個(gè)fragment的是通過同一個(gè)viewmodel進(jìn)行組件之間的通訊,這里值得注意的是兩個(gè)fragment中初始化viewmodel時(shí)傳入的都是getActivity() 這也就意味著他們傳入的是同一個(gè)對(duì)象,如果不同,那么得到的將是兩個(gè)viewmodel對(duì)象,也不會(huì)收到通知進(jìn)行更新了。
這種組件間通訊的好處在于
- activity不需要做任何事情
- fragment不需要知道彼此,而是通過viewmodel進(jìn)行聯(lián)系
Viewmodel分析
我們先來看下它的聲明周期圖:

從上圖我們分析得出,左側(cè)表示Activity的生命周期狀態(tài),右側(cè)綠色部分表示ViewModel的生命周期范圍。當(dāng)屏幕旋轉(zhuǎn)的時(shí)候,Activity會(huì)被recreate,Activity會(huì)經(jīng)過幾個(gè)生命周期方法,但是這個(gè)時(shí)候ViewModel還是之前的對(duì)象,并沒有被重新創(chuàng)建,只有當(dāng)Activity的finish()方法被調(diào)用時(shí),ViewModel.onCleared()方法會(huì)被調(diào)用,對(duì)象才會(huì)被銷毀。這張圖很好的描述了當(dāng)Activity被recreate時(shí),ViewModel的生命周期。
另外,有個(gè)注意的地方:在ViewModel中不要持有Activity的引用。為什么要注意這一點(diǎn)呢?從上面的圖我們看到,當(dāng)Activity被recreate時(shí),ViewModel對(duì)象并沒有被銷毀,如果Model持有Activity的引用時(shí)就可能會(huì)導(dǎo)致內(nèi)存泄漏。那如果你要使用到Context對(duì)象怎么辦呢,ViewModel的子類AndroidViewModel為我們很好的解決了這一問題,我們稍后會(huì)分析。
我們?cè)賮砜匆幌?strong>viewmodel的類圖:

根據(jù)這張類圖,我們來分析一下:
- ViewModelProviders是ViewModel工具類,該類提供了通過Fragment和Activity得到ViewModel的方法,而具體實(shí)現(xiàn)又是由ViewModelProvider實(shí)現(xiàn)的。
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
//param1是ViewModelStore,param2是工廠類
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
//他在of()方法里初始化了ViewModelProvider里的工廠類AndroidViewModelFactory,并renturn了ViewModelProvider對(duì)象
//內(nèi)部還有一些check方法用于檢查Fragment是否Attached to Activity,Activity的Application對(duì)象是否為空等
-
ViewModelStores是ViewModelStore的工廠方法類,它會(huì)關(guān)聯(lián)Fragment,activity
上個(gè)代碼片段我們看見它在of()方法里renturn時(shí)new了一個(gè)provider對(duì)象并通過ViewModelStores.of()得到stroe對(duì)象
//ViewModelStores.of(activity)方法返回了ViewModelStore對(duì)象
return new ViewModelProvider(ViewModelStores.of(activity), factory);
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
//我們看見這里有一個(gè)holderFragmentFor對(duì)象(HolderFragment)
return holderFragmentFor(activity).getViewModelStore();
}
public HolderFragment() {
//將這個(gè)方法設(shè)置為true就可以使當(dāng)前Fragment在Activity重建時(shí)存活下來,如果不設(shè)置或者設(shè)置為false,當(dāng)前Fragment會(huì)在Activity重建時(shí)同樣發(fā)生重建,以至于被新建的對(duì)象所替代。
setRetainInstance(true);
//這樣就解決了旋轉(zhuǎn)屏幕時(shí)因?yàn)橹亟▽?dǎo)致數(shù)據(jù)丟失的問題
}
//在HoldFragment中初始化了ViewModelStore用于在銷毀時(shí)clear,釋放掉viewmodel
private ViewModelStore mViewModelStore = new ViewModelStore();
@Override
public void onDestroy() {
super.onDestroy();
mViewModelStore.clear();
}
@NonNull
@Override
public ViewModelStore getViewModelStore() {
return mViewModelStore;
}
- ViewModelStore是存儲(chǔ)ViewModel的類,具體實(shí)現(xiàn)是通過HashMap來保存ViewModle對(duì)象。
//viewmodelStroe用戶存儲(chǔ)Viewmodel,并提供set,get方法和clear方法
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
public final void clear() {
for (ViewModel vm : mMap.values()) {
//這里調(diào)用了viewmodel的onCleared()方法
vm.onCleared();
}
mMap.clear();
}
}
- ViewModelProvider是實(shí)現(xiàn)ViewModel創(chuàng)建、獲取的工具類。在ViewModelProvider中定義了一個(gè)創(chuàng)建ViewModel的接口類——Factory。ViewModelProvider中有個(gè)ViewModelStore對(duì)象,用于存儲(chǔ)ViewModel對(duì)象。
//構(gòu)造方法中我們傳入了stroe和工廠類
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
//我們使用的.get(modele.class)方法最終會(huì)調(diào)用這個(gè)get方法
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//從map中獲取model對(duì)象
ViewModel viewModel = mViewModelStore.get(key);
//判斷是否是同一個(gè)對(duì)象?如果是return此viewmode
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
} else {
if (viewModel != null) {
// TODO: log a warning.
}
}
//如果不是則通過工廠類創(chuàng)建,然后緩存進(jìn)stroe,并return
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
好了,至此我們通過閱讀源碼,已經(jīng)對(duì)viewmodel的工作原理有了一定的了解,那么們就來總結(jié)一下它如何通過一系列操作,來做到對(duì)view和model進(jìn)行管理的。
//1. 首先我們會(huì)在繼承viewmodel的類中,做一些數(shù)據(jù)操作(初始化livedata),并提供set,get方法返回livedata對(duì)象。(代碼省略...查看開頭基本用法的代碼塊)
//2. 我們?cè)趘iew組件(activity/fragment)中拿到viewmodel對(duì)象
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
//3. of()方法返回viewmodelprodiver對(duì)象
public static ViewModelProvider of(@NonNull FragmentActivity activity,@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
//4. 在of方法里初始化ViewModelProvider中AndroidViewModelFactory對(duì)象
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
//5. ViewModelProvider中傳入ViewModelstore和factory對(duì)象
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
//6. 工廠類 ViewModelStores.of(activity)方法返回ViewModelStore對(duì)象
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
//7.holderFragmentFor對(duì)象(HolderFragment)解決了屏幕旋轉(zhuǎn)時(shí)數(shù)據(jù)保存。setRetainInstance(true);在里面初始化了ViewModelStore對(duì)象
return holderFragmentFor(activity).getViewModelStore();
}
//8. 這時(shí)我們拿到了ViewModelProviders.of(this)返回的provider對(duì)象,然后調(diào)用get方法.get(MyViewModel.class);最終走到此get()方法
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//從map中獲取model對(duì)象
ViewModel viewModel = mViewModelStore.get(key);
//判斷是否是同一個(gè)對(duì)象?如果是return此viewmode
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
} else {
if (viewModel != null) {
// TODO: log a warning.
}
}
//如果不是則通過工廠類創(chuàng)建,然后緩存進(jìn)stroe,并return
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
//9. 返回了viewmodel對(duì)象,通過viewmodel的get方法拿到livedata對(duì)象,并在ui組件處于活躍狀態(tài)時(shí)更新UI
model.getUsers().observe(this, users -> {
// 更新 UI
});
參考&感謝
總結(jié)
Viewmodel的職責(zé)是為UI組件管理數(shù)據(jù)。規(guī)范化viewmodel的使用方式,不要在viewmodel層中持有UI層的引用,避免因viewmodel超長的生命周期,導(dǎo)致內(nèi)存泄漏。實(shí)現(xiàn)UI組件和數(shù)據(jù)間的管理和解耦,才是這個(gè)框架帶給我們的理解。
通過我們對(duì)源碼的分析,它的功能并不復(fù)雜,但設(shè)計(jì)的十分巧妙,背后摻雜的思想和理念才是值得去反復(fù)揣度的。它可以更好的實(shí)現(xiàn)把業(yè)務(wù)代碼下沉到viewmodel中實(shí)現(xiàn),既保證了UI組件中代碼的清爽,又可以實(shí)現(xiàn)對(duì)數(shù)據(jù)的管理。
Viewmodel可以用于activity中不同fragment之間的通信,也可以用作Fragment之間一種解耦方式。
接下來我們會(huì)講到Android架構(gòu)的另一個(gè)組件Room,來看下這個(gè)數(shù)據(jù)庫能帶給我們哪些驚艷?
當(dāng)學(xué)習(xí)完所有的組件后,我們就開始嘗試著去搭一款適合自己的MVVM框架,用于加深我們對(duì)Android架構(gòu)組件的學(xué)習(xí),從而做到學(xué)以致用。
Android架構(gòu)組件系列文章
我的博客(Power)
Android架構(gòu)組件(一):Lifecycle
Android架構(gòu)組件(二):LiveData
Android架構(gòu)組件(三):Viewmodel
Android架構(gòu)組件(四):Room
感謝您的閱讀和支持!