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 get(@NonNull Class 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 get(@NonNull String key, @NonNull Class 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 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 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 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不会造成内存泄露的原因就是这个。