概述
ViewModel,從字面上理解的話,它肯定是跟視圖(View)以及數據(Model)相關的。正像它字面意思一樣,它是負責準備和管理和UI組件(Fragment/Activity)相關的數據類,也就是說ViewModel是用來管理UI相關的數據的,同時ViewModel還可以用來負責UI組件間的通信。
之前存在的問題
ViewModel用來存儲和管理UI相關的數據,可于將一個Activity或Fragment組件相關的數據邏輯抽象出來,并能適配組件的生命周期,如當屏幕旋轉Activity重建后,ViewModel中的數據依然有效。
引入ViewModel之前,存在如下幾個問題:
- 通常Android系統來管理UI controllers(如Activity、Fragment)的生命周期,由系統響應用戶交互或者重建組件,用戶無法操控。當組件被銷毀并重建后,原來組件相關的數據也會丟失,如果數據類型比較簡單,同時數據量也不大,可以通過onSaveInstanceState()存儲數據,組件重建之后通過onCreate(),從中讀取Bundle恢復數據。但如果是大量數據,不方便序列化及反序列化,則上述方法將不適用。
- UI controllers經常會發送很多異步請求,有可能會出現UI組件已銷毀,而請求還未返回的情況,因此UI controllers需要做額外的工作以防止內存泄露。
當Activity因為配置變化而銷毀重建時,一般數據會重新請求,其實這是一種浪費,最好就是能夠保留上次的數據。 - UI controllers其實只需要負責展示UI數據、響應用戶交互和系統交互即可。但往往開發者會在Activity或Fragment中寫許多數據請求和處理的工作,造成UI controllers類代碼膨脹,也會導致單元測試難以進行。我們應該遵循職責分離原則,將數據相關的事情從UI controllers中分離出來。
ViewModel基本使用
public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<Users>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// 異步調用獲取用戶列表
}
}
新的Activity如下:
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// 更新 UI
});
}
}
如果Activity被重新創建了,它會收到被之前Activity創建的相同MyViewModel實例。當所屬Activity終止后,框架調用ViewModel的onCleared()方法清除資源。
因為ViewModel在指定的Activity或Fragment實例外存活,它應該永遠不能引用一個View,或持有任何包含Activity context引用的類。如果ViewModel需要Application的context(如獲取系統服務),可以擴展AndroidViewmodel,并擁有一個構造器接收Application。
在Fragment間共享數據
一個Activity中的多個Fragment相互通訊是很常見的。之前每個Fragment需要定義接口描述,所屬Activity將二者捆綁在一起。此外,每個Fragment必須處理其他Fragment未創建或不可見的情況。通過使用ViewModel可以解決這個痛點,這些Fragment可以使用它們的Activity共享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 onActivityCreated() {
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends LifecycleFragment {
public void onActivityCreated() {
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// update UI
});
}
}
注意:上面兩個Fragment都用到了如下代碼來獲取ViewModel,getActivity()返回的是同一個宿主Activity,因此兩個Fragment之間返回的是同一個SharedViewModel對象。
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
這種方式的好處包括:
- Activity不需要做任何事情,也不需要知道通訊的事情
- Fragment不需要知道彼此,除了SharedViewModel進行聯系。如果它們(Fragment)其中一個消失了,其余的仍然能夠像往常一樣工作。
- 每個Fragment有自己的生命周期,而且不會受其它Fragment生命周期的影響。事實上,一個Fragment替換另一個Fragment,UI的工作也不會受到任何影響。
ViewModel的生命周期
ViewModel對象的范圍由獲取ViewModel時傳遞至ViewModelProvider的Lifecycle所決定。ViewModel始終處在內存中,直到Lifecycle永久地離開—對于Activity來說,是當它終止(finish)的時候,對于Fragment來說,是當它分離(detached)的時候。
上圖左側為Activity的生命周期過程,期間有一個旋轉屏幕的操作;右側則為ViewModel的生命周期過程。
一般通過如下代碼初始化ViewModel:
viewModel = ViewModelProviders.of(this).get(UserProfileViewModel.class);
this參數一般為Activity或Fragment,因此ViewModelProvider可以獲取組件的生命周期。
Activity在生命周期中可能會觸發多次onCreate(),而ViewModel則只會在第一次onCreate()時創建,然后直到最后Activity銷毀。
ViewModel相關類圖
借用Android架構組件(三)——ViewModel的類圖:
ViewModelProviders是ViewModel工具類,該類提供了通過Fragment和Activity得到ViewModel的方法,而具體實現又是由ViewModelProvider實現的。
ViewModelProvider是實現ViewModel創建、獲取的工具類。在ViewModelProvider中定義了一個創建ViewModel的接口類——Factory。ViewModelProvider中有個ViewModelStore對象,用于存儲ViewModel對象。
ViewModelStore是存儲ViewModel的類,具體實現是通過HashMap來保存ViewModle對象。
ViewModel是個抽象類,里面只定義了一個onCleared()方法,該方法在ViewModel不在被使用時調用。ViewModel有一個子類AndroidViewModel,這個類是便于要在ViewModel中使用Context對象,因為我們前面提到不能在ViewModel中持有Activity的引用。
ViewModelStores是ViewModelStore的工廠方法類,它會關聯HolderFragment,HolderFragment有個嵌套類——HolderFragmentManager。
ViewModel相關時序圖
追溯創建一個ViewModel的源碼,會察覺需要的步驟有點多。下面以在Fragment中得到ViewModel對象為例看下整個過程的時序圖。
借用Android架構組件(三)——ViewModel的時序圖:
時序圖看起來比較復雜,但是它只描述了兩個過程:
- 得到ViewModel對象。
- HolderFragment被銷毀時,ViewModel收到onCleared()通知。
ViewModel相關源碼分析
ViewModelProviders類的具體實現:
public class ViewModelProviders {
private static Application checkApplication(Activity activity) {
Application application = activity.getApplication();
if (application == null) {
throw new IllegalStateException("Your activity/fragment is not yet attached to "
+ "Application. You can't request ViewModel before onCreate call.");
}
return application;
}
private static Activity checkActivity(Fragment fragment) {
Activity activity = fragment.getActivity();
if (activity == null) {
throw new IllegalStateException("Can't create ViewModelProvider for detached fragment");
}
return activity;
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment) {
ViewModelProvider.AndroidViewModelFactory factory =
ViewModelProvider.AndroidViewModelFactory.getInstance(
checkApplication(checkActivity(fragment)));
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
ViewModelProvider.AndroidViewModelFactory factory =
ViewModelProvider.AndroidViewModelFactory.getInstance(
checkApplication(activity));
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @NonNull Factory factory) {
checkApplication(checkActivity(fragment));
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@NonNull Factory factory) {
checkApplication(activity);
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
ViewModelProviders提供了四個of()方法,四個方法功能類似,其中of(FragmentActivity activity, Factory factory)和of(Fragment fragment, Factory factory)提供了自定義創建ViewModel的方法。
1. 判斷Fragment的是否Attached to Activity,Activity的Application對象是否為空。
2. 創建ViewModel對象看似很簡單,一行代碼搞定。
new ViewModelProvider(ViewModelStores.of(fragment), factory)
先看看ViewModelStores.of()方法:
@NonNull
@MainThread
public static ViewModelStore of(@NonNull Fragment fragment) {
if (fragment instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) fragment).getViewModelStore();
}
return holderFragmentFor(fragment).getViewModelStore();
}
繼續深入發現其實是實現了一個接口:
public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
}
holderFragmentFor()是HolderFragment的靜態方法,HolderFragment繼承自Fragment。我們先看holderFragment()方法的具體實現
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(Fragment fragment) {
return sHolderFragmentManager.holderFragmentFor(fragment);
}
@NonNull
@Override
public ViewModelStore getViewModelStore() {
return mViewModelStore;
}
繼續看HolderFragmentManager.holderFragmentFor()方法的具體實現
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;
}
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);
}
}
};
private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
HolderFragment holder = new HolderFragment(); // 創建HolderFragment對象
fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
return holder;
}
public HolderFragment() {
//這個是關鍵,這就使得Activity被recreate時,Fragment的onDestroy()和onCreate()不會被調用
setRetainInstance(true);
}
setRetainInstance(boolean) 是Fragment中的一個方法。將這個方法設置為true就可以使當前Fragment在Activity重建時存活下來, 如果不設置或者設置為 false, 當前 Fragment 會在 Activity 重建時同樣發生重建, 以至于被新建的對象所替代。
在setRetainInstance(boolean)為true的 Fragment 中放一個專門用于存儲ViewModel的Map, 自然Map中所有的ViewModel都會幸免于Activity重建,讓Activity, Fragment都綁定一個這樣的Fragment, 將ViewModel存放到這個 Fragment 的 Map 中, ViewModel 組件就這樣實現了。
到此為止,我們已經得到了ViewStore對象,前面我們在創建ViewModelProvider對象是通過這行代碼實現的new ViewModelProvider(ViewModelStores.of(fragment), sDefaultFactory)現在再看下ViewModelProvider的構造方法
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
}
現在就可以通過ViewModelProvider.get()方法得到ViewModel對象,繼續看下該方法的具體實現
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key); //從緩存中查找是否有已有ViewModel對象。
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass); //創建ViewModel對象,然后緩存起來。
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
ViewModelProvider.get()方法比較簡單,注釋中都寫明了。最后我們看下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);
}
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
ViewModelStore是緩存ViewModel的類,put()、get()方法用于存取ViewModel對象,另外提供了clear()方法用于清空緩存的ViewModel對象,在該方法中會調用ViewModel.onCleared()方法通知ViewModel對象不再被使用。
ViewModel收到onCleared()通知
HolderFragment的onDestroy()方法
@Override
public void onDestroy() {
super.onDestroy();
mViewModelStore.clear();
}
在onDestroy()方法中調用了ViewModelStore.clear()方法,我們知道在該方法中會調用ViewModel的onCleared()方法。在你看了HolderFragment源碼后,或許你會有個疑問,mViewModelStore保存的ViewModel對象是在哪里添加的呢? 細心的話,你會發現在ViewModelProvider的構造方法中,已經將HolderFragment中的ViwModelStore對象mViewModelStore的引用傳遞給了ViewModelProvider中的mViewModelStore,而在ViewModelProvider.get()方法中會向mViewModelStore添加ViewModel對象。
總結
- ViewModel職責是為Activity或Fragment管理、請求數據,具體數據請求邏輯不應該寫在ViewModel中,否則ViewModel的職責會變得太重,此處需要一個引入一個Repository,負責數據請求相關工作。具體請參考 Android架構組件。
- ViewModel可以用于Activity內不同Fragment的交互,也可以用作Fragment之間一種解耦方式。
- ViewModel也可以負責處理部分Activity/Fragment與應用其他模塊的交互。
- ViewModel生命周期(以Activity為例)起始于Activity第一次onCreate(),結束于Activity最終finish時。
官方:
https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html