ViewModel的生命周期,以往我們將UI展示的數據直接緩存在對應的UI組件中,遇到ConfigurationChange等事件UI組件重新創建,我們緩存的數據也隨之銷毀。但ViewModel可以在內存中長期被持有而不受ConfigurationChange的影響,直到相關聯的UI組件真正銷毀的時候ViewModel才隨之釋放。
實例的獲取
myViewModel = new ViewModelProvider(this).get(MyViewModel.class);
//myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
第二種已經舍棄。
首先來看ViewModelProvider的構造函數。
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
ViewModelProvider:用于創建 ViewModel,其構造方法有兩個參數,第一個參數傳入 ViewModelStoreOwner ,確定了 ViewModelStore 的作用域,第二個參數為 ViewModelProvider.Factory,用于初始化 ViewModel 對象,默認為 getDefaultViewModelProviderFactory() 方法獲取的 factory
ViewModelStoreOwner是一個接口,其實現類是FragmentActivity和Fragment.先看FragmentActivity中的具體實現。
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;
}
從上面的方法可以看到,mViewModelStore是通過NonConfigurationInstances得到。而NonConfigurationInstances的實例是通過getLastNonConfigurationInstance()獲得。
getLastNonConfigurationInstance()是Activity中的一個方法。
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
也就是說要知道mViewModelStore怎么來的,只需要知道mLastNonConfigurationInstances什么時候賦值的即可。
全局搜索可以知道在attach()中進行賦值。而attach()在ActivityThread的performLaunchActivity()進行調用。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
} catch (Exception e) {
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (r.overrideConfig != null) {
config.updateFrom(r.overrideConfig);
}
appContext.setOuterContext(activity);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken);//1
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;//2
checkAndBlockForNetworkAccess();
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
r.activity = activity;
}
r.setState(ON_CREATE);
synchronized (mResourcesManager) {
mActivities.put(r.token, r);
}
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
return activity;
}
從注釋1可知,在attach方法中將lastNonConfigurationInstances賦值過去,所以整個流程為,handleRelaunchActivity()->handleRelaunchActivityInner()->handleDestroyActivity()->performDestroyActivity()->handleLaunchActivity()->performLaunchActivity()->activity.attach()。
在handleDestroyActivity中,getNonConfigInstance參數為true,則在performDestroyActivity,
if (getNonConfigInstance) {
try {
r.lastNonConfigurationInstances
= r.activity.retainNonConfigurationInstances();
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to retain activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e);
}
}
}
會將lastNonConfigurationInstances進行賦值保存, 這樣在activity 重建時 mLastNonConfigurationInstances 能夠得到上一次的值,使得 ViewModelStore 值不變 。
然后調用get()去獲得ViewModel實例。
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) {
ViewModel viewModel = mViewModelStore.get(key);
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;
}
根據Factory去創建ViewModel,創建成功后并保存在mViewModelStore中。
ViewModelStoreOwner 代表著作用域,其內部唯一的方法返回 ViewModelStore 對象,也即不同的作用域對應不同的 ViewModelStore ,而 ViewModelStore 內部維護著 ViewModel 的 HashMap ,因此只要保證相同作用域的 ViewModelStore 對象相同就能保證相同作用域獲取到相同的 ViewModel 對象。
由于 ViewModel 的設計,使得 activity/fragment 依賴它,而 ViewModel 不依賴 activity/fragment。因此只要不讓 ViewModel 持有 context 或 view 的引用,就不會造成內存泄漏。
viewModelScope
在ViewModel中使用的協程。 它是ViewModel的擴展屬性。
自動取消,不會造成內存泄漏,如果是CoroutineScope,就需要在onCleared()方法中手動取消了,否則可能會造成內存泄漏。
配合ViewModel,能減少樣板代碼,提高效率。
viewModelScope的使用
fun getBannerData(){
viewModelScope.launch (Dispatchers.IO){
//repo.getBanner(bannerLiveData)
}
}
進入viewModelScope的源碼
public val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
return setTagIfAbsent(
JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
)
}
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}
在get()中,通過setTagIfAbsent創建了協程,并且指定主線程。CloseableCoroutineScope實現Closeable接口,并重寫唯一方法close(),并在方法中取消了協程。
<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;
}
在setTagIfAbsent中,以HashMap的形式把協程對象保存起來了,并配有getTag方法。并且調用了closeWithRuntimeException(),這個方法中調用了Closeable接口的close()方法,而close()方法就是用來取消協程的。
而closeWithRuntimeException方法是誰調用的呢,主要是ViewModel中的clear()方法。
@MainThread
final void clear() {
mCleared = true;
if (mBagOfTags != null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
closeWithRuntimeException(value);
}
}
}
onCleared();
}
這里是循環保存協程的HashMap,然后調用closeWithRuntimeException取消協程。
那這個ViewModel中的clear()方法又是誰調用的呢?
查看源碼,只有一處調用,就是在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);
}
Set<String> keys() {
return new HashSet<>(mMap.keySet());
}
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
在上面分析ViewModel的時候,我們知道在創建ViewModel,創建成功后并保存在mViewModelStore中。
回過頭來再看ViewModelStore,同樣也有一個clear()方法,同樣循環調用vm.clear()。而這個是在ComponentActivity.java中調用的:
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();
}
}
}
});
先是獲取Lifecycle,并添加生命周期監聽。
在生命周期為onDestroy的時候,獲取ViewModelStore,并調用其clear()方法。
因此前面說的,ViewModel和ViewModelScope不會造成內存泄露的原因就是這個。