概述
- 在横竖屏切换/系统语言切换的时候都会导致
Activity
的销毁重建,Activity
销毁的时候如果需要保存业务数据需求,一般会在onSaveInstanceState
方法保存Bundle
类型的数据,然后在onCreate
或者onRestoreInstanceState
方法中恢复数据。然而这只能解决数据保存的问题而且必须是Bundle
支持的数据类型才可以,无法解决销毁重建可能带来的生命周期切换带来的内存泄漏的问题。鉴于此,jetpack
推出了ViewModel
解决数据保存/恢复和内存泄漏的问题,这篇文章主要分析ViewModel
在Framwork
层的实现原理。
1.ViewModel使用
- 通常我们会在代码中如下使用
ViewModel
[PersonViewModel]
//1.自定义PersonViewModel实现ViewModel
public class PersonViewModel extends ViewModel {
//2.使用MutableLiveData监听数据的变化,进而做出相应
public MutableLiveData mutableLiveNameData=new MutableLiveData<>();
//3.伴随Activity生命周期OnDestroy销毁的时间ViewModel的回调,用来做善后清理工作
@Override
protected void onCleared() {
super.onCleared();
mutableLiveNameData=null;
}
}
[MainActivity]
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 实例化ViewModel
personViewModel= ViewModelProviders.of(this).get(PersonViewModel.class);
personViewModel.mutableLiveNameData.observe(this, new Observer() {
@Override
public void onChanged(String s) {
//监听数据变化时的回调,做出响应操作
}
});
- 使用方式很简单,不做多余的解释,下面直接
personViewModel= ViewModelProviders.of(this).get(PersonViewModel.class)
分析原理
2. ViewModelProviders
-
ViewModelProviders
是一个辅助类,根据参数activity
调用内部static
方法完成创建ViewModelProvider
[ViewModelProviders]
//1.静态方法,参数是activity实例
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}
//2.根据activity和factory创建ViewModelProvider
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
//2.1 检查application是否为null,若为null,抛出异常
Application application = checkApplication(activity);
if (factory == null) {//2.2默认为null
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
//3.创建ViewModelProvider并返回
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
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;
}
3.ViewModelProvider
[ViewModelProvider]
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
-
ViewModelProvider
构造函数的参数:ViewModelStore
,factory
,下面对这两个参数进行分析。
3.1 ViewModelStore
[ViewModelStore]
//1.存储Activity或者Fragment相关的ViewModel集合
public class ViewModelStore {
private final HashMap mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
//2.当有值覆盖的时候会调用oldViewModel.onCleared()
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());
}
/**
* 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();
}
}
-
ViewModelStore
使用HashMap
存储Activity
或Fragment
实例相关联的所有ViewModel -
key
是ViewModel
的全类名
,value是开发者自定义PersonViewModel实例 - 在
Activity
生命周期onDestroy
的时候判断是否是由于配置改变造成的destroy,如果不是会调用ViewModelStore
的clear
方法,遍历map
中所有的ViewModel
实例,调用ViewModel
的clear
方法,最后调用mMap.clear
方法清除集合中的所有元素。
3.2 Factory
- 创建
ViewModel
的工厂接口,这里使用了设计模式中的静态工厂设计模式
public interface Factory {
@NonNull
T create(@NonNull Class modelClass);
}
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static AndroidViewModelFactory sInstance;
/**
* Retrieve a singleton instance of AndroidViewModelFactory.
*
* @param application an application to pass in {@link AndroidViewModel}
* @return A valid {@link AndroidViewModelFactory}
*/
@NonNull
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
private Application mApplication;
/**
* Creates a {@code AndroidViewModelFactory}
*
* @param application an application to pass in {@link AndroidViewModel}
*/
public AndroidViewModelFactory(@NonNull Application application) {
mApplication = application;
}
@NonNull
@Override
public T create(@NonNull Class modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
//noinspection TryWithIdenticalCatches
try {
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);
}
}
}
public static class NewInstanceFactory implements Factory {
@SuppressWarnings("ClassNewInstance")
@NonNull
@Override
public T create(@NonNull Class 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);
}
}
}
根据开发者自定的
ViewModel
类型选择不同的工厂反射创建ViewModel
实例若开发者自定义
ViewModel
继承的是ViewModel
则使用NewInstanceFactory
工程创建若开发者自定义
ViewModel
继承的是AndroidViewModel
则使用AndroidViewModelFactory
工程创建,内部可以获取一个全局的Application
应用实例,为了防止调用上下文不当造成内存泄漏
.
经过上面的分析知道了创建ViewModel的流程,接下俩看下activity实例怎么关联ViewModelStore的。
[ViewModelPrividers]
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
//获取activity关联的ViewModelStore
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
4. Activity
- 查看
activity.getViewModelStore
方法 - 由于
Activity
继承自ComponentActivity
所以这里查看的是ComponentActivity
[ComponentActivity]
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) {
//这里调用了getLastNonConfigurationInstance获取旋转配置变化前保存的数据,即恢复之前保存的数据
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
/ * @return the object previously returned by {@link #onRetainNonConfigurationInstance()}
*/
//这个注释告诉我们,数据保存是在onRetainNonConfigurationInstance()方法中
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
-
ativitity
实例调用getLastNonConfigurationInstance
方法获取config change配置变化前保存的NonConfigurationInstances
实例nc
,进而通过nc
恢复关联的viewModelStore
对象。 - 调用
onRetainNonConfigurationInstance
方法完成NonConfigurationInstances
实例对象的保存。
4.1 NonConfigurationInstance
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
-
NonConfigurationInstances
保存了activity
关联的ViewModelStore
实例。
此时不禁要想下,对NonConfigurationInstances 数据保存的方法onRetainNonConfigurationInstance方法在什么时机调用的呢?恢复数据的NonConfigurationInstances实例数据又是在哪里赋值的呢?
5. ActivityThread & Activity
- 了解过Framework源码的人对这个类再熟悉不过了,四大组件的创建以及的它们生命周期的回调都是它内部实现的。
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback,
AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient {
@UnsupportedAppUsage
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进行了数据恢复
mLastNonConfigurationInstances = lastNonConfigurationInstances;
}
public final class ActivityThread extends ClientTransactionHandler {
// 省略其他代码...
//这里是执行了Activity的onDestory回调时触发的操作
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
// 省略其他代码...
ActivityClientRecord r = mActivities.get(token);
if (getNonConfigInstance) {//1.首先判断是否有LastNonConfigInstance实例
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);
}
}
}
return r;
}
}
- 经过上面的源码分析,
mLastNonConfigurationInstances
的恢复是在Activity.attach
方法中。 -
retainNonConfigurationInstances()
方法的调用是在ActivityThread.performDestroy()
中。
6.ViewModelStore中的数据什么时候会回收?
- 上面说了在
Activity#onDestory
生命周期的时候会回收activity
实例关联的内存中所有ViewModel
数据,是不是这样呢?我们可以在源码中找到答案:
[ComponentActivity]
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.");
}
if (Build.VERSION.SDK_INT >= 19) {
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_STOP) {
Window window = getWindow();
final View decor = window != null ? window.peekDecorView() : null;
if (decor != null) {
decor.cancelPendingInputEvents();
}
}
}
});
}
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
//如果不是因为配置变化引起的activity销毁就会销毁ViewModelStore中的所有ViewModel数据
getViewModelStore().clear();
}
}
}
});
if (19 <= SDK_INT && SDK_INT <= 23) {
getLifecycle().addObserver(new ImmLeaksCleaner(this));
}
}
- 监听
Activity.onDestroy
事件,如果不是因为配置变化引起的,比如屏幕旋转/系统字体切换
引起的,即Activity
是用户触发了返回键
操作或者程序中调用了finish
就清空activity
实例关联的所有ViewModel
数据并回调其'clear'方法。
总结
- 使用
ViewModel
保存ui数据
可以极大的提高我们的开发效率,不再担心因为配置变化引起的数据保存恢复问题,同时也可以避免一定程度发生的内存泄漏问题
,而且可以很轻松的实现Activity
与Fragment
之间数据共享,以及Fragment
和Fragment
数据交互的问题.