ViewModel
所以Activity或Fragment中的一些數據也會隨着銷燬而丟失,隨着重構而重新生成。比如你的Activity中有個用戶列表,當這個Activity重構的時候,新的Activity會重新獲取用戶列表。對於一些簡單的數據,Activity可以使用onSaveInstanceState()
方法,並從onCreate的bundle中重新獲取。但這一方法途徑僅僅適合一些簡單的UI狀態,對於用戶列表這種龐大的數據並不適合。
還存在一個問題,Activity或者Fragment經常會做一些異步的耗時操作。隨之就需要Activity和Fragment管理這些異步操作,並在自己被destroyed的時候清理它們,從而保證內存溢出這類問題的發生。這樣的處理會隨着項目擴大而變得十分複雜,一不留神,你的App就Crash了。
Activity和Fragment本身需要處理很多用戶的輸入事件並和操作系統打交道,所以當它們還要花時間管理它們的數據資源時,class文件就會變得異常龐大,然後就會造就出所謂的god
activities
和god
fragments
。這些UI控制類僅僅靠一個class就能處理相關的所有事務。簡直跟上帝沒啥兩樣。但這些類如果要進行單元測試的話,那就尷尬了。
所以就有了MVC
,MVP
這類設計模式,將視圖與數據分離。今天講到的ViewModel
類的功能也一樣,就是講數據從UI中分離出來。並且當Activity或Fragment重構的時候,ViewModel會自動保留之前的數據並給新的Activity或Fragment使用。對於上面提到的用戶列表的例子,ViewModel會爲我們很好的管理這些數據。
ViewModel 示例:
public class UserModel extends ViewModel { public final LiveData<User> userLiveData = new LiveData<>(); public UserModel() { // trigger user load. } void doAction() { // depending on the action, do necessary business logic calls and update the // userLiveData. } }
接着在我們的Activity中就能這樣使用了:
public class UserActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.user_activity_layout); final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class); viewModel.userLiveData.observer(this, new Observer() { @Override public void onChanged(@Nullable User data) { // update ui. } }); findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { viewModel.doAction(); } }); } }
這是當MyActivity被重構時,獲得到的model實例是與重構前同一個,當MyActivity被銷燬時,Framework會調用ViewModel的onCleared()
,我們就可以在此方法中做資源的清理。
因爲ViewModel的生命週期是和Activity或Fragment分開的,所以在ViewModel中絕對不能引用任何View對象或者任何引用了Activity的Context的對象。如果ViewModel中需要Application的Context的話,可以繼承
AndroidViewModel
。
Fragment之間的數據共享
在Activity中包好多個Fragment並且需要相互通信是非常常見的,這時就需要這些Fragment定義一些接口,然後讓Activity來進行協調。而且這些Fragment還需要處理其他Fragment不可見或者還沒有創建這些細節問題。
上面這個動點可以被ViewModel輕易解決,想象意向有這麼個Activity,它包含FragmentA和FragmentB,其中A是用戶列表,B是用戶的詳細數據,點擊列表上的某個用戶,在B中顯示相應的數據。
看看使用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.
});
}
}
這裏要注意的是兩個Fragment都使用了getActivity
作爲參數來獲得ViewModel實例。這表示這兩個Fragment獲得的ViewModel對象是同一個。
使用了ViewModel的好處如下:
- Activity不需要做任何事情,不需要干涉這兩個Fragment之間的通信。
- Fragment不需要互相知道,即使一個消失不可見,另一個也能很好的工作。
- Fragment有自己的生命週期,它們之間互不干擾,即便你用一個FragmentC替代了B,FragmentA也能正常工作,沒有任何問題。
ViewModel的生命週期
ViewModel的生命週期跟着傳遞給ViewModelProvider
的LifeCycle
走,當生成了ViewModel的實例後,它會一直待在內存中,直到對應的LifeCycle徹底結束。下面是ViewModel與Activity對應的生命週期圖:
最後附官方url:https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html