Android - 带着问题看源码之 ViewModel,安卓软件android

本文探讨Android中如何在页面配置变更时保存和恢复ViewModel的数据,涉及onSaveInstanceState、onRetainNonConfigurationInstance等方法,以及如何避免内存泄漏和在Fragment间共享数据的问题。建议使用ViewModel来管理页面数据,以实现更高效的保存和恢复。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(2) 页面配置修改后如何保存数据


源码: Android API 29 查看顺序:AppCompatActivity—>FragmentActivity—>ComponentActivity

AppCompatActivity.java 同样的这里也只是委托了事件

@Override

protected void onSaveInstanceState(@NonNull Bundle outState) {

super.onSaveInstanceState(outState);

getDelegate().onSaveInstanceState(outState);

}

FragmentActivity.java 中处理了数据的保存

@Override

protected void onSaveInstanceState(@NonNull Bundle outState) {

super.onSaveInstanceState(outState);

markFragmentsCreated();

mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);

Parcelable p = mFragments.saveAllState();

if (p != null) {

outState.putParcelable(FRAGMENTS_TAG, p);

}

if (mPendingFragmentActivityResults.size() > 0) {

outState.putInt(NEXT_CANDIDATE_REQUEST_INDEX_TAG, mNextCandidateRequestIndex);

int[] requestCodes = new int[mPendingFragmentActivityResults.size()];

String[] fragmentWhos = new String[mPendingFragmentActivityResults.size()];

for (int i = 0; i < mPendingFragmentActivityResults.size(); i++) {

requestCodes[i] = mPendingFragmentActivityResults.keyAt(i);

fragmentWhos[i] = mPendingFragmentActivityResults.valueAt(i);

}

outState.putIntArray(ALLOCATED_REQUEST_INDICIES_TAG, requestCodes);

outState.putStringArray(REQUEST_FRAGMENT_WHO_TAG, fragmentWhos);

}

}

ComponentActivity.java 的 onSaveInstanceState 方法中还是没有 ViewModel 的身影,但是紧接着的方法 onRetainNonConfigurationInstance 的注释有点意思,它是 final 所以不能重写,但是它里面又有一个公开的 onRetainCustomNonConfigurationInstance 方法,所以说我们可以重写该方法返回一些 Object 的对象,但是该方法过时了。

推荐我们使用 ViewModel 来实现。

@CallSuper

@Override

protected void onSaveInstanceState(@NonNull Bundle outState) {

Lifecycle lifecycle = getLifecycle();

if (lifecycle instanceof LifecycleRegistry) {

((LifecycleRegistry) lifecycle).setCurrentState(Lifecycle.State.CREATED);

}

super.onSaveInstanceState(outState);

mSavedStateRegistryController.performSave(outState);

}

/**

  • Retain all appropriate non-config state. You can NOT

  • override this yourself! Use a {@link androidx.lifecycle.ViewModel} if you want to

  • retain your own non config state.

*/

@Override

@Nullable

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;

}

所以我们还是回到 ComponentActivity.java 的构造方法中找到 Lifecycle.Event.ON_DESTROY 的处理,

在里面有个方法 getViewModelStore 获取到了 ViewModel 的数据,它的实现如下:

@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;

}

如果不使用 ViewModel,在页面销毁和重建时可以用重写下面两个方法保存和恢复数据

  • onSaveInstanceState

  • onRestoreInstanceState

但是我们知道这两个方法只能保存 Bundle 支持的数据,要想保存其它数据就得用到下面的方法了

  • onRetainNonConfigurationInstance:final 所以不能重写,作用是保存数据,调用时机是onStop()onDestroy()之间

  • onRetainCustomNonConfigurationInstance:@Deprecated 过时了,推荐使用 ViewModel

  • getLastNonConfigurationInstance:作用是取出数据,调用时机是onCreate()之后

  • getLastCustomNonConfigurationInstance:@Deprecated 过时了,推荐使用 ViewModel

如果使用ViewModel,在页面销毁和重建时就不需要你操心数据的保存和恢了。

ViewModel会在onRetainNonConfigurationInstance 方法中保存数据,如下:

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;

}

}

如果Activity重写了onRetainCustomNonConfigurationInstance方法还可以将自定义的Object类型的对象保存到NonConfigurationInstances 的 custom属性中。如下:

NonConfigurationInstances nci = new NonConfigurationInstances();

nci.custom = custom;

nci.viewModelStore = viewModelStore;

并且还会在ActivityonDestory生命周期判断页面是否重建,重建则不删除ViewModel中的数据

(3) 如何避免内存泄漏?


源码: Android API 29

首先我们要知道内存为何会泄漏。

短生命周期的对象被长生命周期的对象一直持有,导致短生命周期的对象在本该被回收的时候没有及时回收,导致内存泄漏。

比如:

内部类导致的内存泄漏如:非静态内部类 Handler、匿名内部类 Thread 中存在业务处理的时间比 Activity 的生命周期还长,这就会导致内存泄漏

因为内部类默认会持有外部类的引用,当外部类 Activity 关闭需要回收的时候,它的内部类还在持有它,导致它没有被回收。

在文章开头ViewModel的使用中用到了LiveData库。 它是具有生命感知的数据存储类,这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。

活跃生命周期状态也就是用户可见的状态下才会更新数据。详细的实现后面深入源码查看并写篇文章记录。

又因为ViewModel会在onDestory之后会自动清除相关数据,结合这两点即可避免内存泄漏。

(4) 如何在 Activity 中的两个或更多 Fragment 共享数据?


在创建 ViewModel 的时候需要传入this上下文,这个上下文是个LifecycleOwner, AppCompatActivity 和 Fragment都默认实现了LifecycleOwner接口,因此可以在Activity或者Fragment中直接传递this

在传递上下文给 ViewModel之后,会通过ViewModelStore里面的HashMap去根据不同的key取出不同的ViewModel, 而这里的 keyandroidx.lifecycle.ViewModelProvider.DefaultKey:加上ViewModel类所在的包含路径的全名称字符串。

Activity中的ViewModel,除非被杀死或者关闭,否则一直存在。 这也就是为什么Activity中的多个Fragment能共享ViewModel中的数据。

主要代码如下:

/**

  • Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or

  • an activity), associated with this {@code ViewModelProvider}.

  • The created ViewModel is associated with the given scope and will be retained

  • as long as the scope is alive (e.g. if it is an activity, until it is

  • finished or process is killed).

  • @param key The key to use to identify the ViewModel.

  • @param modelClass The class of the ViewModel to create an instance of it if it is not

  •               present.
    
  • @param The type parameter for the ViewModel.

  • @return A ViewModel that is an instance of the given type {@code T}.

*/

@NonNull

最后

由于文章篇幅原因,我只把面试题列了出来,详细的答案,我整理成了一份PDF文档,这份文档还包括了还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 ,帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习。

需要的朋友可以私信我【答案】或者点击这里免费领取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值