前言
Jetpack ViewModel相信大家都很熟悉了,ViewModel是一個保存Activity、Fragment數據的類,它不隨著Activity的配置改變而銷毀,例如常見的屏幕旋轉、系統語言切換、深、淺色模式切換等
ViewModel創建時,會綁定一個Activity或Fragment,所以可以實現單Activity多Fragment共享數據,使用ViewModel一般會配套使用LiveData
簡單使用
以登錄頁面為例,只有一個用戶名和密碼的輸入框,用戶名和密碼數據存放在ViewModel的LiveData中
- 定義LoginViewModel
/**
* 登錄ViewModel
*/
public class LoginViewModel extends ViewModel {
/**
* 用戶名
*/
public final MutableLiveData<String> mUserNameLiveData = new MutableLiveData<>();
/**
* 密碼
*/
public final MutableLiveData<String> mPasswordLiveData = new MutableLiveData<>();
}
- 獲取LoginViewModel實例
LoginViewModel loginViewModel = ViewModelProviders.of(this).get(LoginViewModel.class);
- 監聽EditText的文字改變,在改變時,更新LiveData
vUsernameTextField.addTextChangedListener(new TextChangedListener() {
@Override
public void onTextChanged(CharSequence text, int start, int before, int count) {
mLoginViewModel.mUserNameLiveData.setValue(text);
}
//...
});
vPasswordTextField.addTextChangedListener(new TextChangedListener() {
@Override
public void onTextChanged(CharSequence text, int start, int before, int count) {
mLoginViewModel.mPasswordLiveData.setValue(text);
}
//...
});
- 監聽LiveData數據變化,做響應的業務邏輯
mLoginViewModel.mUserNameLiveData.observe(this, new Observer<String>() {
@Override
public void onChanged(String newUserName) {
//...
}
});
mLoginViewModel.mPasswordLiveData.observe(this, new Observer<String>() {
@Override
public void onChanged(String newPassword) {
//...
}
});
源碼分析
從示例代碼來看,關鍵點就在ViewModelProviders.of(activity)
方法,以及傳入activity或fragment后,再調用get(clazz)
方法獲取ViewModel的實例,所以切入點就在這2個API上
ViewModelProviders.of(activity)
,轉調了of(activity,factory)方法,判斷如果沒有傳入Factory實例,則使用默認的AndroidViewModelFactory
隨后,創建了ViewModelProvider實例,同時調用Activity的getViewModelStore()方法獲取ViewModelStore
我們來看下這個ViewModelStore是什么東西
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
Application application = checkApplication(activity);
//沒有指定工廠,則使用默認的AndroidViewModelFactory
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
//創建ViewModelProvider實例,并調用Activity的getViewModelStore()方法獲取ViewModelStore
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
//檢查Activity的Application是否為空
private static Application checkApplication(Activity activity) {
Application application = activity.getApplication();
if (application == null) {
throw new IllegalStateException("Your activity/fragment is not yet attached to "
+ "Application. You can't request ViewModel before onCreate call.");
}
return application;
}
ViewModelStoreOwner
ViewModelStoreOwner是一個接口,ComponentActivity、AppCompatActivity、Fragment都實現了它
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
ViewModelStore
ViewModelStore,看名字是一個存儲ViewModel的類,get()
API就是調用的它,類結構來看,就是持有一個HashMap,通過一個key,來獲取對應的value,就是ViewModel的實例,接下來看ViewModelProvider
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());
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
ViewModelProvider
get()
方法,key為ViewModel類的類名,優先從mViewModelStore
中獲取,而mViewModelStore
相當于一個Map緩存的功能,獲取得到則返回,獲取不到則通過傳入的工廠進行創建
常用工廠有2種
- NewInstanceFactory,反射調用無參構造實例化ViewModel
- AndroidViewModelFactory,反射調用Application有參構造實例化ViewModel
public class ViewModelProvider {
/**
* 緩存Key的前綴
*/
private static final String DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey";
/**
* ViewModel工廠接口
*/
public interface Factory {
/**
* 創建指定Class的ViewModel
*/
<T extends ViewModel> T create(Class<T> modelClass);
}
static class OnRequeryFactory {
void onRequery(ViewModel viewModel) {
}
}
/**
* 支持傳入指定Key和Class,創建ViewModel
*/
abstract static class KeyedFactory extends OnRequeryFactory implements Factory {
/**
* 可以指定Key和Class創建的create方法,舊的create(modelClass)將不再支持
*/
public abstract <T extends ViewModel> T create(String key,
Class<T> modelClass);
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
throw new UnsupportedOperationException("create(String, Class<?>) must be called on "
+ "implementaions of KeyedFactory");
}
}
private final Factory mFactory;
private final ViewModelStore mViewModelStore;
/**
* 構造方法
*
* @param owner ViewModelStore持有者,如果它實現了HasDefaultViewModelProviderFactory接口,則通過它來獲取默認工廠
*/
public ViewModelProvider(ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
/**
* 構造方法,傳入ViewModelStore和ViewModel工廠Factory
*/
public ViewModelProvider(ViewModelStoreOwner owner, Factory factory) {
this(owner.getViewModelStore(), factory);
}
/**
* 構造方法,傳入ViewModelStore和ViewModel工廠Factory
*/
public ViewModelProvider(ViewModelStore store, Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
/**
* 獲取指定的ViewModel
*/
public <T extends ViewModel> T get(Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
//默認緩存Key是前綴 + ClassName
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
/**
* 獲取指定的ViewModel
*
* @param key 緩存Key
* @param modelClass ViewModel的Class
*/
@SuppressWarnings("unchecked")
public <T extends ViewModel> T get(String key, Class<T> modelClass) {
//先從緩存中獲取
ViewModel viewModel = mViewModelStore.get(key);
//檢查取出的實例是否和Class匹配
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(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;
}
/**
* 簡單工廠,會調用無參構造實例化ViewModel
*/
public static class NewInstanceFactory implements Factory {
/**
* 單例
*/
private static NewInstanceFactory sInstance;
/**
* 獲取工廠實例
*/
static NewInstanceFactory getInstance() {
if (sInstance == null) {
sInstance = new NewInstanceFactory();
}
return sInstance;
}
@SuppressWarnings("ClassNewInstance")
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
try {
//通過反射調用無參構造進行創建
return modelClass.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
}
/**
* AndroidViewModel的工廠
*/
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
/**
* 單例
*/
private static AndroidViewModelFactory sInstance;
/**
* 獲取工廠實例
*/
public static AndroidViewModelFactory getInstance(Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
private final Application mApplication;
public AndroidViewModelFactory(Application application) {
mApplication = application;
}
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
//使用前,先判斷傳進來的Class,是否是AndroidViewModel或其子類
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
//noinspection TryWithIdenticalCatches
try {
//調用只有一次參數,并且參數類型時Application的構造方法
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
return super.create(modelClass);
}
}
}
ViewModel
經過ViewModelProvider的get()
方法,就成功創建ViewModel的實例了,接下來看看ViewModel
類
主要有一個clear()
清理方法,它將在Activity或Fragment的onDestory()中調用,用于清理ViewModel上保存的數據,數據保存到mBagOfTags
變量,它是一個Map,所以有setTagIfAbsent()
保存數據方法,getTag()
獲取數據方法
其中,如果保存的對象實現了Closeable
接口,就會調用它的close()
方法,例如IO流對象就實現了Closeable
接口,也可以自定義對象實現該接口,統一在close()
方法中,關閉資源等
public abstract class ViewModel {
/**
* 存儲可Closeable的Map,存儲的Closeable實例,會在onCleared()時調用它們的close()方法
*/
private Map<String, Object> mBagOfTags = new HashMap<>();
private volatile boolean mCleared = false;
/**
* ViewModel即將被銷毀時回調
*/
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
/**
* 清理方法
*/
final void clear() {
mCleared = true;
// Since clear() is final, this method is still called on mock objects
// and in those cases, mBagOfTags is null. It'll always be empty though
// because setTagIfAbsent and getTag are not final so we can skip
// clearing it
if (mBagOfTags != null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
// see comment for the similar call in setTagIfAbsent
closeWithRuntimeException(value);
}
}
}
onCleared();
}
/**
* 保存一個對象到ViewModel,同時指定它的Key,如果已經保存過了,則不會再保存,會返回保存的實例
*/
@SuppressWarnings("unchecked")
<T> T setTagIfAbsent(String key, T newValue) {
T previous;
synchronized (mBagOfTags) {
previous = (T) mBagOfTags.get(key);
if (previous == null) {
mBagOfTags.put(key, newValue);
}
}
T result = previous == null ? newValue : previous;
//如果已經銷毀了,則關閉
if (mCleared) {
// It is possible that we'll call close() multiple times on the same object, but
// Closeable interface requires close method to be idempotent:
// "if the stream is already closed then invoking this method has no effect." (c)
closeWithRuntimeException(result);
}
return result;
}
/**
* 從ViewModel上,獲取保存的對象
*/
@SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
<T> T getTag(String key) {
if (mBagOfTags == null) {
return null;
}
synchronized (mBagOfTags) {
return (T) mBagOfTags.get(key);
}
}
/**
* 關閉Closeable實例
*/
private static void closeWithRuntimeException(Object obj) {
if (obj instanceof Closeable) {
try {
((Closeable) obj).close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
ViewModel的clear()方法調用時機
在ComponentActivity
的構造方法中,注冊了一個Lifecycle監聽,在onDestroy()
回調時,判斷如果沒有發生配置改變而發生的重建,才清理ViewModelStore,所以Activity在重建時,不會清理ViewModel,數據是保留的,當正常按返回鍵銷毀時,才會清理ViewModel
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
//noinspection ConstantConditions
if (lifecycle == null) {
throw new IllegalStateException("getLifecycle() returned null in ComponentActivity's "
+ "constructor. Please make sure you are lazily constructing your Lifecycle "
+ "in the first call to getLifecycle() rather than relying on field "
+ "initialization.");
}
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
//銷毀時,沒有發生配置改變而重建時,才清理ViewModelStore
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
}
ViewModel的保存和恢復
在Activity因配置改變而銷毀重建時,ComponentActivity
的mViewModelStore
變量肯定會被置空,那ViewModel從哪里恢復呢?我們來看看getViewModelStore()
方法
- ViewModel恢復
mViewModelStore
為null時,調用getLastNonConfigurationInstance()
獲取一個NonConfigurationInstances
對象nc
,拿取對象的viewModelStore屬性來對mViewModelStore
進行賦值
NonConfigurationInstances
就是一個配置類,保存一個自定義對象custom
和viewModelStore
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
//ComponentActivity的NonConfigurationInstances類
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
既然恢復點知道了,那么保存點在哪呢?
- ViewModel的保存
保存點,就是retainNonConfigurationInstances()方法,該方法在ActivityThread
類的performDestroyActivity()
方法中調用,并把該方法的返回結果保存在ActivityClientRecord
對象中
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
Class<? extends Activity> activityClass = null;
if (r != null) {
activityClass = r.activity.getClass();
r.activity.mConfigChangeFlags |= configChanges;
if (finishing) {
r.activity.mFinished = true;
}
performPauseActivityIfNeeded(r, "destroy");
if (!r.stopped) {
callActivityOnStop(r, false /* saveState */, "destroy");
}
if (getNonConfigInstance) {
try {
//保存點,調用Activity的retainNonConfigurationInstances()方法獲取 NonConfigurationInstances對象
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
} catch (Exception e) {
}
}
return r;
}
值得注意的是,Activity上也有一個NonConfigurationInstances
類,它和子類ComponentActivity
上的NonConfigurationInstances
類不是同一個,關系是父類Activity的NonConfigurationInstances
類的activity
屬性,保存了ComponentActivity
的NonConfigurationInstances
類對象
//Activity類上的NonConfigurationInstances配置類
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
//Activity類上的retainNonConfigurationInstances()方法
NonConfigurationInstances retainNonConfigurationInstances() {
//調用onRetainNonConfigurationInstance(),獲取需要保存的自定義對象
Object activity = onRetainNonConfigurationInstance();
HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
mFragments.doLoaderStart();
mFragments.doLoaderStop(true);
ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
if (activity == null && children == null && fragments == null && loaders == null
&& mVoiceInteractor == null) {
return null;
}
//創建NonConfigurationInstances配置類
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity;
nci.children = children;
nci.fragments = fragments;
nci.loaders = loaders;
if (mVoiceInteractor != null) {
mVoiceInteractor.retainInstance();
nci.voiceInteractor = mVoiceInteractor;
}
return nci;
}
- onRetainNonConfigurationInstance(),Activity的空方法,被子類
ComponentActivity
復寫
public Object onRetainNonConfigurationInstance() {
return null;
}
- ComponentActivity,子類復寫該方法,保存自己的數據到父類的
NonConfigurationInstances
類中
其中,回調了一個onRetainCustomNonConfigurationInstance()
方法,這個方法是提供給子類保存自定義對象的,所以如果我們有需求在配置重建時,保存一個對象,則可以復寫該方法進行返回
@Override
@Nullable
public final Object onRetainNonConfigurationInstance() {
//提供自定義對象給子類復寫,保存自定義對象
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
//如果viewModelStore為null,獲取最后一次保存的配置,如果獲取到配置,則從配置中獲取viewModelStore實例
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
//從配置中獲取不到,并且沒有自定義對象保存,則返回null
if (viewModelStore == null && custom == null) {
return null;
}
//需要保存,則創建NonConfigurationInstances實例,保存自定義對象和viewModelStore
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
- getLastNonConfigurationInstance(),獲取保存的NonConfigurationInstances對象
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
- Activity的attach()方法
我們知道,Activity的attach()
方法,由ActivityThread
會調用performLaunchActivity()
方法
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
//給配置類進行賦值
mLastNonConfigurationInstances = lastNonConfigurationInstances;
}
總結
ViewModel的保存和恢復流程如下
保存:Activity因配置發生改變,
ActivityThread
會調用performDestroyActivity()
方法,該方法調用了retainNonConfigurationInstances()
方法,方法返回一個NonConfigurationInstances
對象,該對象保存了ViewModel實例,然后NonConfigurationInstances
對象被保存在了ActivityClientRecord
中恢復:Activity重建后,
ActivityThread
會調用performLaunchActivity()
方法,該方法會調用Activity的attach()
方法,該方法又會從ActivityClientRecord
中把NonConfigurationInstances
對象給Activity賦值