一酌呆、ViewModel的特點(diǎn):
- ViewModel 可以實(shí)現(xiàn)Activity與fragment之間的數(shù)據(jù)共享,以及同一個(gè)Activity中多個(gè)Fragment之間的數(shù)據(jù)共享
- ViewModel可以感知Activity的生命周期啼辣,actiivty ondestory前躏敢,會調(diào)用viewModel的onCleared()方法 供我們來釋放資源
二、viewModel是如何實(shí)現(xiàn) 數(shù)據(jù)共享和感知生命周期的
我們知道ViewModel 是一個(gè)抽象類岗照,自定義ViewModel是需繼承ViewModel類.
public abstract class ViewModel {
@Nullable
private final Map<String, Object> mBagOfTags = new HashMap<>();
private volatile boolean mCleared = false;
/**
* This method will be called when this ViewModel is no longer used and will be destroyed.
* <p>
* It is useful when ViewModel observes some data and you need to clear this subscription to
* prevent a leak of this ViewModel.
*/
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
@MainThread
final void clear() {
mCleared = true;
//省略代碼...
onCleared();
}
TranslateViewModel.kt
如TranslateViewModel 繼承自ViewModel類
class TranslateViewModel constructor() : ViewModel(){
}
初始化如TranslateViewModel對象,卻不能直接使用new TranslateViewModel()來初始化該對象蚕钦。
需要調(diào)用ViewModelProviders.of(this).get(TranslateViewModel::class.java) 方法
錯(cuò)誤的初始化方法:
mViewModel = new TranslateViewModel()
正確的初始化方法:
mViewModel = ViewModelProviders.of(this).get(TranslateViewModel::class.java)
2.1 如何實(shí)現(xiàn)數(shù)據(jù)共享的
關(guān)鍵點(diǎn)就在于
mViewModel = ViewModelProviders.of(this).get(TranslateViewModel::class.java)
2.1.1亭病、ViewModelProviders.of() 返回的是一個(gè)ViewModelProvider
public class ViewModelProviders {
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
}
2.1.2、ViewModelProvider用于提供一個(gè)ViewModel嘶居,構(gòu)建一個(gè)ViewModelProvider,需要兩個(gè)參數(shù)
- ViewModelStore 提供ViewModel的存儲和復(fù)用機(jī)制
- ViewModelProvider.Factory - ViewModel的生產(chǎn)工廠罪帖,用于生產(chǎn)一個(gè)新的ViewModel對象
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
我們看一下ViewModelProvider.get()方法,此方法用戶返回一個(gè)viewModel對象
public class ViewModelProvider {
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//關(guān)鍵代碼
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
}
可以發(fā)現(xiàn)
(1)會先去mViewModelStore 查找,有沒有XXX::class 對應(yīng)的ViewModel對象邮屁,如果有整袁,則直接返回
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
(2)如果mViewModelStore中沒有 目標(biāo)ViewModel對象,則直接利用ViewModelProvider.Factory 生產(chǎn)一個(gè)新的ViewModel對象佑吝,同時(shí)保存到mViewModelStore中坐昙,以供下次復(fù)用。
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
mViewModelStore.put(key, viewModel);
2.1.3芋忿、ViewModelStore 用于存儲復(fù)用ViewModel對象炸客,
同時(shí)FragmentActivity 和Fragment都繼承了ViewModelStoreOwner ,可以獲取到viewModelStore對象。
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);
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
總結(jié):
至此原理清楚了戈钢。
- 每個(gè)Activity或者Fragment當(dāng)中持有一個(gè)mViewModelStore嚷量,存儲和復(fù)用activity當(dāng)中用到的ViewModel
- 創(chuàng)建ViewModel對象時(shí),如果傳入的是同一個(gè)Activity,那么ViewModelProvider中的ViewModelStore就是同一個(gè)對象逆趣。
- ViewModelProvider調(diào)用get(XXXViewModel::class) ,獲取ViewModel時(shí),通過Activity的ViewModelStore實(shí)現(xiàn)了ViewModel的共享嗜历。
即 傳入的Activity是同一個(gè)的前提下宣渗,多次調(diào)用下面的方法抖所,獲取到的ViewModel是同一個(gè)對象。
mViewModel = ViewModelProviders.of(this).get(TranslateViewModel::class.java)
2.2 如何感知Activity的生命周期的
首先明確一點(diǎn)痕囱,ViewModel只能感知activity生命周期中的onDestory()田轧。在activity銷毀前,執(zhí)行viewModel的clear(回調(diào)onCleared()鞍恢,進(jìn)行資源的釋放)傻粘。
那么我們來看activity中是在什么時(shí)候觸發(fā)viewModel的clear()操作的
public class FragmentActivity extends ComponentActivity implements XXX{
}
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner {
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
//noinspection ConstantConditions
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
}
}
可以看到,在基類ComponentActivity的構(gòu)造方法中,注冊了LifeCycleObserver帮掉,檢測到Lifecycle.Event.ON_DESTROY事件之后弦悉,會自動調(diào)用getViewModelStore().clear()
public class ViewModelStore {
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}