作用:
ViewModel 類旨在以注重生命周期的方式存儲和管理界面相關的數據。
問題:
(1) Android 框架可以管理界面控制器(如 Activity, Fragment)的生命周期。由Android 框架響應的某些用戶操作或設備事件,用戶無法控制。
當系統銷毀或重新創建界面控制器,則存儲在其中的任何臨時性界面相關數據都會丟失。對於簡單的數據,Activity可以使用onSaveInstanceState()存儲數據,組件重建后從onCreate()中的Bundle恢復其數據,但此方法僅適合可以序列化再反序列化的少量數據,如果數據量較大,如用戶列表或位圖,則不適合。
(2)界面控制器經常需要進行異步調用,這些調用可能需要一些時間才能返回結果,有可能會出現UI組件已銷毀,而請求還未返回的情況。在因配置更改而重新創建對象的情況下,對象可能需要重新發送請求,會造成資源的浪費
(3)應該遵循職責分離原則,將視圖數據從界面控制器邏輯中分離出來,這樣不會導致界面控制器類代碼膨脹
ViewModel的用法:
架構組件為界面控制器提供了 ViewModel 輔助程序類,該類負責為界面准備數據。
在配置更改期間會自動保留 ViewModel 對象,以便它們存儲的數據立即可供下一個Activity/Fragment實例使用。如以下示例代碼所示:
public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<User>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
可以從 Activity 訪問該列表
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same MyViewModel instance created by the first activity.
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// update UI
});
}
}
如果Activity被重新創建了,它會收到被之前Activity創建的相同MyViewModel實例。當所屬Activity終止后,框架調用ViewModel的onCleared()方法清除資源。
因為ViewModel在指定的Activity或Fragment實例外存活,它應該永遠不能引用一個View,或持有任何包含Activity context引用的類。如果ViewModel需要Application的context(如獲取系統服務),可以擴展AndroidViewmodel,並擁有一個構造器接收Application。
ViewModel 的生命周期
ViewModel 對象存在的時間范圍是獲取ViewModel時傳遞給ViewModelProvider的Lifecycle。ViewModel將一直留在內存中,直到限定其存在時間范圍的Lifecycle 永久消失:對於 Activity,是在Activity完成時;而對於Fragment,是在Fragment分離時。
上圖左側為Activity的生命周期過程,期間有一個旋轉屏幕的操作;右側則為ViewModel的生命周期過程。
一般通過如下代碼初始化ViewModel:
viewModel = ViewModelProviders.of(this).get(UserProfileViewModel.class);
// this參數一般為Activity或Fragment,因此ViewModelProvider可以獲取組件的生命周期。
Activity在生命周期中可能會觸發多次onCreate(),而ViewModel則只會在第一次onCreate()時創建,然后直到最后Activity銷毀。
在Fragment間共享數據
一個Activity中的多個Fragment相互通訊是很常見的。假設有一個Fragment,在該Fragment 中,用戶從列表中選擇一項,還有另一個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 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都會檢索包含它們的Activity。這樣,當這兩個Fragment各自獲取ViewModelProvider時,它們會收到相同的SharedViewModel實例(其范圍限定為該Activity)。
此方法具有以下優勢:
* Activity 不需要執行任何操作,也不需要對此通信有任何了解。
* 除了SharedViewModel約定之外,Fragment不需要相互了解。如果其中一個Fragment 消失,另一個Fragment將繼續照常工作。
* 每個Fragment 都有自己的生命周期,而不受另一個Fragment的生命周期的影響。如果一個Fragment替換另一個Fragment,界面將繼續工作而沒有任何問題。
參考:
https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html#java
https://blog.csdn.net/qq_24442769/article/details/79426609
https://blog.csdn.net/zhuzp_blog/article/details/78910535