前言
上篇我們分析了Livedata的使用及原理做院,相信我們已經(jīng)學(xué)會了使用Livedata來存儲數(shù)據(jù)夺脾,并在觀察者組件中實現(xiàn)回調(diào)方法有鹿,來動態(tài)更新UI數(shù)據(jù)。這里奉上(雙膝已經(jīng)跪爛了...)上兩篇的地址:
Android架構(gòu)組件(一):Lifecycle
Android架構(gòu)組件(二):LiveData
方便大家進行查閱和回顧央勒。
那么不见,接下來我們要學(xué)習(xí)我們的第三個架構(gòu)組件——Viewmodel,我們從字面上理解崔步,它肯定和view脖祈,model有關(guān)聯(lián),它是負責(zé)準備和管理UI組件(activity/fragment)相關(guān)的數(shù)據(jù)類刷晋,也就是說Viewmodel是用來管理UI相關(guān)數(shù)據(jù)的盖高,同時Viewmodel還可以負責(zé)UI間組件的通訊。
Viewmodel是什么眼虱?
我們已經(jīng)知道喻奥,Viewmodel有以下兩點作用:
- 用來管理數(shù)據(jù)(model)和UI組件(view)的數(shù)據(jù)類
- 負責(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
});
}
}
用法很簡單,
我們在viewmodel中定義一個livedata的集合,通過網(wǎng)絡(luò)獲取數(shù)據(jù)后捏悬,調(diào)用setValue方法通知觀察者(UI)在活躍狀態(tài)下時更新數(shù)據(jù)撞蚕。
在activity中,我們初始化viewmodel拿到livedata并在他的onchanged()方法里做UI相關(guān)操作过牙。
這里我們發(fā)現(xiàn)viewmodel做了一個中間人的角色甥厦,它管理著model與view之間相互關(guān)聯(lián)的數(shù)據(jù),這樣我們就可以把數(shù)據(jù)相關(guān)(model)操作放到viewmodel中寇钉,把UI操作放到view中刀疙,完全由viewmodel管理,使model與view層完全解耦扫倡。
-
負責(zé)UI間組件之間的通訊
一個activity中的多個Fragment互相間通訊時很常見的需求谦秧,我們可以使用activity中的viewmodel來實現(xiàn)fragment之間數(shù)據(jù)的共享。
下面這個例子也很簡單:
//我們定義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中我們通過同一個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));
}
}
上述代碼我們知道了疚鲤,兩個fragment的是通過同一個viewmodel進行組件之間的通訊,這里值得注意的是兩個fragment中初始化viewmodel時傳入的都是getActivity() 這也就意味著他們傳入的是同一個對象缘挑,如果不同集歇,那么得到的將是兩個viewmodel對象,也不會收到通知進行更新了语淘。
這種組件間通訊的好處在于
- activity不需要做任何事情
- fragment不需要知道彼此诲宇,而是通過viewmodel進行聯(lián)系
Viewmodel分析
我們先來看下它的聲明周期圖:
從上圖我們分析得出际歼,左側(cè)表示Activity的生命周期狀態(tài),右側(cè)綠色部分表示ViewModel的生命周期范圍焕窝。當(dāng)屏幕旋轉(zhuǎn)的時候,Activity會被recreate维贺,Activity會經(jīng)過幾個生命周期方法它掂,但是這個時候ViewModel還是之前的對象,并沒有被重新創(chuàng)建溯泣,只有當(dāng)Activity的finish()方法被調(diào)用時,ViewModel.onCleared()方法會被調(diào)用,對象才會被銷毀扛禽。這張圖很好的描述了當(dāng)Activity被recreate時坤学,ViewModel的生命周期。
另外肢簿,有個注意的地方:在ViewModel中不要持有Activity的引用靶剑。為什么要注意這一點呢?從上面的圖我們看到池充,當(dāng)Activity被recreate時桩引,ViewModel對象并沒有被銷毀,如果Model持有Activity的引用時就可能會導(dǎo)致內(nèi)存泄漏收夸。那如果你要使用到Context對象怎么辦呢坑匠,ViewModel的子類AndroidViewModel為我們很好的解決了這一問題,我們稍后會分析卧惜。
我們再來看一下viewmodel的類圖:
根據(jù)這張類圖厘灼,我們來分析一下:
- ViewModelProviders是ViewModel工具類,該類提供了通過Fragment和Activity得到ViewModel的方法咽瓷,而具體實現(xiàn)又是由ViewModelProvider實現(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對象
//內(nèi)部還有一些check方法用于檢查Fragment是否Attached to Activity围来,Activity的Application對象是否為空等
-
ViewModelStores是ViewModelStore的工廠方法類,它會關(guān)聯(lián)Fragment匈睁,activity
上個代碼片段我們看見它在of()方法里renturn時new了一個provider對象并通過ViewModelStores.of()得到stroe對象
//ViewModelStores.of(activity)方法返回了ViewModelStore對象
return new ViewModelProvider(ViewModelStores.of(activity), factory);
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
//我們看見這里有一個holderFragmentFor對象(HolderFragment)
return holderFragmentFor(activity).getViewModelStore();
}
public HolderFragment() {
//將這個方法設(shè)置為true就可以使當(dāng)前Fragment在Activity重建時存活下來,如果不設(shè)置或者設(shè)置為false,當(dāng)前Fragment會在Activity重建時同樣發(fā)生重建,以至于被新建的對象所替代监透。
setRetainInstance(true);
//這樣就解決了旋轉(zhuǎn)屏幕時因為重建導(dǎo)致數(shù)據(jù)丟失的問題
}
//在HoldFragment中初始化了ViewModelStore用于在銷毀時clear,釋放掉viewmodel
private ViewModelStore mViewModelStore = new ViewModelStore();
@Override
public void onDestroy() {
super.onDestroy();
mViewModelStore.clear();
}
@NonNull
@Override
public ViewModelStore getViewModelStore() {
return mViewModelStore;
}
- ViewModelStore是存儲ViewModel的類航唆,具體實現(xiàn)是通過HashMap來保存ViewModle對象胀蛮。
//viewmodelStroe用戶存儲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是實現(xiàn)ViewModel創(chuàng)建粪狼、獲取的工具類退腥。在ViewModelProvider中定義了一個創(chuàng)建ViewModel的接口類——Factory。ViewModelProvider中有個ViewModelStore對象再榄,用于存儲ViewModel對象狡刘。
//構(gòu)造方法中我們傳入了stroe和工廠類
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
//我們使用的.get(modele.class)方法最終會調(diào)用這個get方法
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//從map中獲取model對象
ViewModel viewModel = mViewModelStore.get(key);
//判斷是否是同一個對象?如果是return此viewmode
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
} else {
if (viewModel != null) {
// TODO: log a warning.
}
}
//如果不是則通過工廠類創(chuàng)建困鸥,然后緩存進stroe嗅蔬,并return
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
好了,至此我們通過閱讀源碼疾就,已經(jīng)對viewmodel的工作原理有了一定的了解澜术,那么們就來總結(jié)一下它如何通過一系列操作,來做到對view和model進行管理的猬腰。
//1. 首先我們會在繼承viewmodel的類中鸟废,做一些數(shù)據(jù)操作(初始化livedata),并提供set,get方法返回livedata對象姑荷。(代碼省略...查看開頭基本用法的代碼塊)
//2. 我們在view組件(activity/fragment)中拿到viewmodel對象
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
//3. of()方法返回viewmodelprodiver對象
public static ViewModelProvider of(@NonNull FragmentActivity activity,@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
//4. 在of方法里初始化ViewModelProvider中AndroidViewModelFactory對象
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
//5. ViewModelProvider中傳入ViewModelstore和factory對象
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
//6. 工廠類 ViewModelStores.of(activity)方法返回ViewModelStore對象
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
//7.holderFragmentFor對象(HolderFragment)解決了屏幕旋轉(zhuǎn)時數(shù)據(jù)保存盒延。setRetainInstance(true);在里面初始化了ViewModelStore對象
return holderFragmentFor(activity).getViewModelStore();
}
//8. 這時我們拿到了ViewModelProviders.of(this)返回的provider對象,然后調(diào)用get方法.get(MyViewModel.class);最終走到此get()方法
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//從map中獲取model對象
ViewModel viewModel = mViewModelStore.get(key);
//判斷是否是同一個對象鼠冕?如果是return此viewmode
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
} else {
if (viewModel != null) {
// TODO: log a warning.
}
}
//如果不是則通過工廠類創(chuàng)建兰英,然后緩存進stroe,并return
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
//9. 返回了viewmodel對象供鸠,通過viewmodel的get方法拿到livedata對象畦贸,并在ui組件處于活躍狀態(tài)時更新UI
model.getUsers().observe(this, users -> {
// 更新 UI
});
參考&感謝
總結(jié)
Viewmodel的職責(zé)是為UI組件管理數(shù)據(jù)。規(guī)范化viewmodel的使用方式楞捂,不要在viewmodel層中持有UI層的引用薄坏,避免因viewmodel超長的生命周期,導(dǎo)致內(nèi)存泄漏寨闹。實現(xiàn)UI組件和數(shù)據(jù)間的管理和解耦胶坠,才是這個框架帶給我們的理解。
通過我們對源碼的分析繁堡,它的功能并不復(fù)雜沈善,但設(shè)計的十分巧妙,背后摻雜的思想和理念才是值得去反復(fù)揣度的椭蹄。它可以更好的實現(xiàn)把業(yè)務(wù)代碼下沉到viewmodel中實現(xiàn)闻牡,既保證了UI組件中代碼的清爽,又可以實現(xiàn)對數(shù)據(jù)的管理绳矩。
Viewmodel可以用于activity中不同fragment之間的通信罩润,也可以用作Fragment之間一種解耦方式。
接下來我們會講到Android架構(gòu)的另一個組件Room翼馆,來看下這個數(shù)據(jù)庫能帶給我們哪些驚艷割以?
當(dāng)學(xué)習(xí)完所有的組件后金度,我們就開始嘗試著去搭一款適合自己的MVVM框架,用于加深我們對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
感謝您的閱讀和支持!