作用儲存和管理UI相關的資料,並且與宣告週期關聯。ViewModel保證資料在配置改變時能夠存活,如螢幕旋轉。
引入的原因
- 如果系統銷燬或者重新建立UI元件,那麼儲存的與UI相關的瞬時資料就會丟失。舉例來說,你的應用可能在Activity中儲存了一個使用者列表。當Activity因為配置改變重建時,新的Activity需要重新獲取使用者列表。針對簡單的資料,Activity可以用
onSaveInstanceState()
方法儲存並在onCreate()
重建這些資料,但是這個方法只是適用於少量的可以被序列化和反序列化的資料,而不適用於大量的資料例如使用者或者點陣圖的列表。 - 另一個問題是UI元件會經常需要非同步呼叫,它們需要花費一些時間。UI元件需要管理這些非同步呼叫,並且確保系統能夠在元件被銷燬時能夠回收這些呼叫,以避免記憶體洩漏。這些管理需要大量的維護工作,同時這些物件在配置改變時會重新建立,這是一種資源浪費,因為這些物件已經建立了,但是又必須重新建立。
- 像Activity和Fragment這樣的UI元件的首頁目的是展示UI資料,響應使用者行為,以及處理作業系統的行為,如許可權申請。UI元件也需要負責從資料庫或者網路載入資料,這會使類膨脹。將過多的責任分配給UI元件會導致一個類需要獨立處理很多工作,而不是將這些工作委託給其他類。把過多的工作分配給UI元件也會導致測試變得困難。
使用:
定義ViewModel
public class UserViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Do an asynchronous operation to fetch users.
}
static class User {
String name;
int age;
}
}
複製程式碼
在Activity中獲取一個例項
UserViewModel model = null;
model = new ViewModelProvider(this).get(UserViewModel.class);
model.getUsers().observe(this, new Observer<List<UserViewModel.User>>() {
@Override
public void onChanged(List<UserViewModel.User> users) {
}
});
複製程式碼
在多個Fragment中共享ViewModel
//通過activity的ViewModelStore獲取ViewModel
new ViewModelProvider(getActivity()).get(UserViewModel.class);
複製程式碼
類圖:
關鍵類:
ViewModel:資料管理類,以及處理Activity和Fragment對資料的互動操作
public abstract class ViewModel {
//為ViewModel儲存一些資料
private final Map<String, Object> mBagOfTags = new HashMap<>();
//onDestroy時標記資料已被清除
private volatile boolean mCleared = false;
}
複製程式碼
ViewModelStore:儲存ViewModel
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);
}
Set<String> keys() {
return new HashSet<>(mMap.keySet());
}
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
複製程式碼
ViewModelProvider:獲取ViewModel
public class ViewModelProvider {
//ViewModel的建立工廠
private final Factory mFactory;
//儲存ViewModel
private final ViewModelStore mViewModelStore;
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
//獲取對應的ViewModel物件
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
//如果ViewModelStore中不存在,則使用Factory建立
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
}
複製程式碼
Factory:建立ViewlMode例項
public interface Factory {
@NonNull //建立ViewModel例項
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
複製程式碼
實現Factory介面的有KeyedFactory,NewInstanceFactory,AndroidViewModelFactory等例項建立類
Activity的重建時的資料恢復過程:
資料儲存過程
-
在ComponentActivity的onSaveInstanceState()中儲存:
-
SavedStateRegistryController中呼叫SavedStateRegistry進行儲存
-
SavedStateRegistry執行儲存邏輯
①AbstractSavedStateViewModelFactory建立的物件最終會被新增到mComponents中②在資料恢復時,mRestoredState會從取出儲存的components。當呼叫AbstractSavedStateViewModelFactory的create方法時,會從mRestoredState中獲取對應的bundle,並remove
如何去使用這些資料的邏輯有點複雜,可以看AbstractSavedViewModelFactory的create方法:
資料恢復過程
- ComponentActivity的onCreate執行恢復:
- SavedStateRegistryController中呼叫SavedStateRegistry的performRestore進行恢復:
- SavedStateRegistry執行恢復邏輯 取出儲存時key為SAVED_COMPONENTS_KEY對應的bundle