ViewModel 類是被設計用來存儲和管理 UI 相關的數據,以便在配置更改(如:屏幕旋轉)時數據能夠保留下來。javascript
應用程序組件(如:activity 和 fragment)具備一個由 Android Framework 管理的生命週期。Framework 可能會徹底不受控制的根據某些用戶操做或設備事件來決定銷燬或從新建立它們。java
因爲這些對象有可能被操做系統銷燬或從新建立,因此保存在它們中的任何數據都會丟失。例如:若是 activity 中有一個用戶列表,當 activity 由於配置更改而從新建立時,新的 activity 必須從新獲取用戶列表。對於簡單的數據,activity 可使用 onSaveInstanceState() 方法從 onCreate() 中的 bundle 裏恢復數據,可是這種方式只適用於少許數據(如:UI 狀態),不適用於大量數據(如:用戶列表)。android
另外一個問題是,這些 UI 控制器(activity,fragment 等)常常須要發起一些須要必定時間才能返回的異步調用。UI 控制器須要管理這些調用而且在其被銷燬時清理它們避免潛在的內存泄漏。這須要大量的維護工做,而且在因爲配置更改致使對象被從新建立的狀況下十分浪費資源,由於須要從新發起相同的請求。git
最後單也很重要的是,這些 UI 控制器已經須要完成響應用戶操做和處理操做系統通訊的工做了。當它們還須要手動處理其資源時,將會使類變的臃腫,創造「萬能 activity」(或「萬能 fragment」);也就是說,一個單獨的類視圖本身處理應用程序的全部工做,而不是將工做委派給其它類。這將會使測試很是困難。github
將視圖數據的全部權從 UI 控制器的邏輯中分離出來是簡單高效的。Lifecycle 提供了一個叫 ViewModel 的新類,一個 UI 控制器的幫助類,用來爲 UI 準備數據。在配置更改期間,ViewModel 會自動保留,以便其保存的數據可以當即提供給下一個 activity 或 fragment 實例。在咱們上面提到的例子中,獲取並持有數據是 ViewModel 的責任,而不是 activity 或 fragment 的。app
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 結束後,Framework 會調用 ViewModel 的 onCleared() 方法來清理資源。ide
注:因爲 ViewModel 存活的比個別的 activity 和 fragment 實例,因此它決不能引用 View,或任何持有 activity context 的引用的類。若是 ViewModel 須要 Application 的 context(如:調用系統服務),能夠繼承 AndroidViewModel 類,能夠在構造函數中接受 Application(由於 Application 繼承了 Context)。函數
activity 中的兩個或多個 fragment 須要相互通訊是很常見的。這不是個簡單的事情,全部的 fragment 都須要定義一些接口秒素,而且擁有它們的 activity 必須將二者綁定在一塊兒。另外,全部的 fragment 必須處理其它的 fragment 沒有被建立或不可見的狀況。
使用 ViewModel 能夠解決這個常見的痛點。假設一個主從式 fragment 的常見狀況,用戶從一個 fragment 的列表裏選中一項,另外一個 fragment 顯示所選項的內容。
這些 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 ->
// 更新 UI
});
}
}複製代碼
請注意,在獲取 ViewModelProvider 時兩個 fragment 都使用 getActivity() 方法。這意味着它們都將會收到被 activity 限定的同一個 SharedViewModel 實例。
這種方式的優勢有:
activity 不須要作或知道關於該通信的任何事情。
除了 SharedViewModel 協議以外 fragment 不須要了解彼此。若是其中一個消失,另外一個會照常工做。
每一個 fragment 有本身的生命週期,而且不受其它 fragment 的生命週期影響。實際上,在 UI 中一個 fragment 替換另外一個 fragment,UI 的運行沒有任何問題。
在獲取 ViewModel 時,ViewModel 對象被傳遞給 ViewModelProvider 的 Lifecycle 限定。ViewModel 保留在內存中,直到限定它的 Lifecycle 永久消失(當 activity 結束或 fragment 被分離)。