public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return new ViewModelProvider(activity);
}
ViewModelProvider 类需要我们传递 ViewModelStore
与 Factory
对象。其构造函数声明如下:
//使用ViewModelStoreOwner对象构造函数
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
//使用ViewModelStoreOwner与Factory对象的构造函数
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
//使用ViewModelStore与Factory对象的构造函数
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
在 ViewModelProvider 内部,拥有三种类型构造函数:
(ViewModelStoreOwner owner)
:- 该构造函数使用 owner 对象的
getViewModelStore()
方法来获取ViewModelStore
对象,如果传入的 owner 对象也实现了HasDefaultViewModelProviderFactory
接口时,那么会调用getDefaultViewModelProviderFactory()
方法获取 Factory。反之,使用内部静态的NewInstanceFactory
对象来创建 Factory 对象。 (ViewModelStoreOwner owner, Factory factory)
:- 该构造函数使用 owner 对象的
getViewModelStore()
方法来获取ViewModelStore
对象,使用传递的 Factory 对象 (ViewModelStore store, Factory factory)
:- 使用
ViewModelStore
与Factory
对象的构造函数
Factory 接口介绍
在 ViewModelProvider 中,Factory
主要用于创建 ViewModel,Factory 的声明如下:
public interface Factory {
/**
- 通过给定的Class对象创建ViewModel对象
-
- @param modelClass 所需ViewModel的Class对象
- @param ViewModel的泛型参数
- @return 新创建的ViewModel对象
*/
@NonNull
T create(@NonNull Class modelClass);
}
通过实现 Factory 接口,我们可以实现自己想要的工厂以创建所需的 ViewModel。在 Android 中有多个类都实现了该接口(如 KeyedFactory, AndroidViewModelFactory)
,这里以默认的 NewInstanceFactory
为例:
public static class NewInstanceFactory implements Factory {
private static NewInstanceFactory sInstance;
@NonNull
static NewInstanceFactory getInstance() {
if (sInstance == null) {
sInstance = new NewInstanceFactory();
}
return sInstance;
}
@SuppressWarnings(“ClassNewInstance”)
@NonNull
@Override
public T create(@NonNull Class modelClass) {
try {
//默认使用对应ViewModel类无参的构造函数创建实例对象
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);
}
}
}
默认情况下, NewInstanceFactory
会调用 ViewModel 的无参构造函数创建实例对象,当然如果你需要在 ViewModel 中使用其他参数,你也可以传递自定义的 Factory。
ViewModelStore 介绍
ViewModelStore 内部维护了一个 HashMap,其 key 为 DEFAULT_KEY
+ ViewModel的Class对象底层类规范名称
,其 value 为对应 ViewModel
对象。每个 Activity 与 Fragment 都对应着一个 ViewModelStore
,用于存储所需的 ViewModel。ViewModelStore 类声明如下所示:
DEFAULT_KEY 值为:“androidx.lifecycle.ViewModelProvider.DefaultKey”
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 keys() {
return new HashSet<>(mMap.keySet());
}
/**
- 当内部的 ViewModel 不再使用时,清除所占的内存
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
//下面调用ViewModel的clear方法
vm.clear();
}
mMap.clear();
}
}
Activity 中创建与获取 ViewModel 流程
ViewModel 最终的创建与获取,需要 ViewProvider 类调用 get(Class<T> modelClass)
方法(该方法内部通过 ViewModelStore 与 Factory 的配合,创建并保存了所需的 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);
}
该方法内部会调用 get(String key, Class<T> modelClass)
方法:
public T get(@NonNull String key, @NonNull Class modelClass) {
//👇根据key值从ViewModelStore中取对应的ViewModel
ViewModel viewModel = mViewModelStore.get(key);
//👇判断所传入的Class对象是否是ViewModel的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.
}
}
//👇如果为null,根据传入的Factory创建新的VideModel
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
//👇将新的 ViewModel 存入ViewModelStore,并返回
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
在该方法中,会在 ViewModelStore 中根据传入的 key 获取并保存 ViewModel。其具体逻辑如下:
- 根据 key 值从 ViewModelStore 中取对应的 ViewModel。
- 判断所传入的 Class 对象是否是 ViewModel 的 Class 类或其子类的对象,如果是,直接返回。(当
Object.isInstance(class)
接受的参数为null
时,该方法会返回false
) - 如果获取的 ViewModel 为 null,会根据传入的 Factory 对象创建新的 VideModel,并将创建好的 ViewModel 放入 ViewModelStore中。
Activity 中创建与获取 ViewModel 的整体流程如下所示:
ViewModel 在 Activity 中不会因配置改变而销毁的原理
我们都知道 ViewModel 不会因为 Activity 的配置发生改变而销毁,ViewModel 作用域如下所示:
观察上图,我相信小伙伴们肯定有如下疑惑:
- 当 Activity 因配置发生改变时,系统会重新创建一个新的 Activity 。那老的 Activity 中的 ViewModel 是如何传递给新的 Activity 的呢?
- ViewModel 又是如何感知配置是否改变,进而判断是否销毁的呢?
要解决如上问题,我们需要了解 Android 中数据恢复的方式以及 Activity 生命周期中 ViewModel 实际处理流程。
数据恢复的常见方式
在 Android 系统中,需要数据恢复有如下两种场景:
- 场景1:资源相关的配置发生改变导致 Activity 被杀死并重新创建。
- 场景2:资源内存不足导致低优先级的 Activity 被杀死。
针对上述场景,分别对应三种不同的数据恢复方式。
对应场景1,不考虑在清单文件中配置
android:configChanges
的特殊情况。
使用 onSaveInstanceState 与 onRestoreInstanceState
使用 onSaveInstanceState 与 onRestoreInstanceState 方法,能处理场景1与场景2的情况。当你的界面数据简单且轻量时,例如原始数据类型或简单对象(比如 String),则我们可以采用该方式。如果你需要恢复的数据较为复杂,那你应该考虑使用 ViewModle + onSaveInstanceState()
(为什么要配合使用,会在下文进行讲解),因为使用 onSaveInstanceState() 会导致序列化或反序列化,而这,有一定的时间消耗。
onSaveInstanceState() 更为详细的介绍以及使用,可参考官方文档:
使用 Fragment 的 setRetainInstance
当配置发生改变时,Fragment 会随着宿主 Activity 销毁与重建,当我们调用 Fragment 中的 setRetainInstance(true)
方法时,系统允许 Fragment 绕开销毁-重建
的过程。使用该方法,将会发送信号给系统,让 Activity 重建时,保留 Fragment 的实例。需要注意的是:
- 使用该方法后,不会调用 Fragment 的
onDestory()
方法,但仍然会调用onDetach()
方法 - 使用该方法后,不会调用 Fragment 的
onCreate(Bundle)
方法。因为 Fragment 没有被重建。 - 使用该方法后,Fragment 的
onAttach(Activity)
与onActivityCreated(Bundle)
方法仍然会被调用。
以下示例代码展示了如何在配置发生改变时,保留 Fragment 实例,并进行数据的恢复。
public class MainActivity extends AppCompatActivity {
private SaveFragment mSaveFragment;
public static final String TAG_SAVE_FRAGMENT = “save_fragment”;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
mSaveFragment = (SaveFragment) fm.findFragmentByTag(TAG_SAVE_FRAGMENT);
// fragment 不为空,是因为配置发生改变,Fragment 被重建
if (mSaveFragment == null) {
mSaveFragment = SaveFragment.newInstance();
fm.beginTransaction().add(mSaveFragment, TAG_SAVE_FRAGMENT).commit();
}
//获取保存的数据
int saveData = mSaveFragment.getSaveData();
}
}
Fragment :
public class SaveFragment extends Fragment {
private int saveData;
public static SaveFragment newInstance() {
Bundle args = new Bundle();
SaveFragment fragment = new SaveFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//保存当前Fragment实例
setRetainInstance(true);
saveData = 1010;//通过网络请求或查询数据库,赋值需要保存的数据
}
@Override
public void onDetach() {
super.onDetach();
}
public int getSaveData() {
return saveData;
}
}
关于 Fragment 的 setRetainInstance 更多用法与注意事项,可以参看文章
Handling Configuration Changes with Fragments
使用 onRetainNonConfigurationInstance 与 getLastNonConfigurationInstance
在 Activity 中提供了 onRetainNonConfigurationInstance
方法,用于处理配置发生改变时数据的保存。随后在重新创建的 Activity 中调用 getLastNonConfigurationInstance
获取上次保存的数据。我们不能直接重写上述方法,如果想在 Activity 中自定义想要恢复的数据,需要我们调用上述两个方法的内部方法:
onRetainCustomNonConfigurationInstance()
getLastCustomNonConfigurationInstance()
注意:
onRetainNonConfigurationInstance
方法系统调用时机介于onStop - onDestory
之间,getLastNonConfigurationInstance
方法可在onCreate
与onStart
方法中调用。
以下代码展示了,在 Actiity 中恢复自定义的数据:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String name = (String) getLastCustomNonConfigurationInstance();
if (!TextUtils.isEmpty(name)) {
//获取恢复后的数据,执行相应操作
}
}
//你可以可以在onStart中,获取恢复的数据
// @Override
// protected void onStart() {
// super.onStart();
// String name = (String) getLastCustomNonConfigurationInstance();
// if (!TextUtils.isEmpty(name)) {
// }
// }
@Nullable
@Override
public Object onRetainCustomNonConfigurationInstance() {
return “AndyJennifer”;
}
}
在 Android 3.0 后,官方推荐使用 Fragment#setRetainInstance(true)
的方式进行数据的恢复。之所以推荐这种方式,个人猜测是为了降低 Activity 的冗余,将数据恢复的任务从 Activity 抽离出来,这更符合单一职责的设计模式。
几种数据恢复方式的总结
通过了解数据恢复的几种方式,我们能得到如下对比图:
ViewModel 的恢复
ViewModel 在官方设计之初就倾向于在配置改变时进行数据的恢复。考虑到数据恢复时的效率,官方最终采用了 onRetainNonConfigurationInstance
的方式来恢复 ViewModel 。
在 SDK 27 之前,官方一直采用
Fragment#setRetainInstance(true)
的方式恢复数据。导致官方修改了其内部实现的原因,猜测是因为 Fragment 的坑,程序的扩展性等其他因素。
知道了 ViewModel 的恢复方式,那现在一起来解决我们之前的疑惑。当 Activity 因配置发生改变时,系统会重新创建一个新的 Activity 。那老的 Activity 中的 ViewModel 是如何传递给新的 Activity ?
在 Androidx 中的 Activity 的最新代码中,官方重写了 onRetainNonConfigurationInstance 方法,在该方法中保存了 ViewModelStore
(ViweModelStore 中存储了 ViewModel ),进而也保存了 ViewModel,具体代码如下所示:
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
//将ViewModel存储在 NonConfigurationInstances 对象中
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
最后
我见过很多技术leader在面试的时候,遇到处于迷茫期的大龄程序员,比面试官年龄都大。这些人有一些共同特征:可能工作了5、6年,还是每天重复给业务部门写代码,工作内容的重复性比较高,没有什么技术含量的工作。问到这些人的职业规划时,他们也没有太多想法。
其实30岁到40岁是一个人职业发展的黄金阶段,一定要在业务范围内的扩张,技术广度和深度提升上有自己的计划,才有助于在职业发展上有持续的发展路径,而不至于停滞不前。
不断奔跑,你就知道学习的意义所在!
《Android高级架构师面试指导+2021大厂面试真题》免费领取
我见过很多技术leader在面试的时候,遇到处于迷茫期的大龄程序员,比面试官年龄都大。这些人有一些共同特征:可能工作了5、6年,还是每天重复给业务部门写代码,工作内容的重复性比较高,没有什么技术含量的工作。问到这些人的职业规划时,他们也没有太多想法。
其实30岁到40岁是一个人职业发展的黄金阶段,一定要在业务范围内的扩张,技术广度和深度提升上有自己的计划,才有助于在职业发展上有持续的发展路径,而不至于停滞不前。
不断奔跑,你就知道学习的意义所在!
[外链图片转存中…(img-eouz1X1Y-1710666232005)]
《Android高级架构师面试指导+2021大厂面试真题》免费领取
[外链图片转存中…(img-jWv6VNq2-1710666232005)]