文章

ViewModel源码研究之聊聊onSaveInstanceState和onRetainNonConfigurationInstance的区别

ViewModel源码研究之聊聊onSaveInstanceState和onRetainNonConfigurationInstance的区别

本文转载自 ViewModel源码研究之聊聊onSaveInstanceState和onRetainNonConfigurationInstance的区别(作者:字节小站)。版权归原作者所有,此处仅作个人学习备份。

  1. 前言

最近在研究ViewModel实现原理。ViewModel有两个特性。

  1. 当配置发生改变时(例如:旋转屏幕),重新创建的Activity能够通过ViewModel将数据还原回来,
  2. 当按返回键或者调用finish方法时,ViewModel能够感知到onDestroy事件,同时将ViewModel保存的Closeable对象关闭掉(例如:主动关闭协程

当屏幕旋转时,会调用ActivityonRetainNonConfigurationInstance方法。ViewModel组件正是通过该方法将ViewModel保存起来,给重建的Activity使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//androidx.activity:activity:1.2.2@aar
//ComponentActivity.java

public final Object onRetainNonConfigurationInstance() {
    // Maintain backward compatibility.
    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;
}

Activity还有个类似的方法onSaveInstanceState()onSaveInstanceState()onRetainNonConfigurationInstance() 的区别是:

  1. onSaveInstanceState() 调用的场景是:activity1启动activity2。生命周期调用顺序如下:

activity1.onPause()->activity2.onCreate()->activity2.onStart()->activity2.onResume()->activity1.onStop()->activity1.onSaveInstanceState(),

  1. onRetainNonConfigurationInstance() 调用场景是当configuration发生改变时,例如:旋转屏幕。

那么问题来了,一共有三个

  1. 它们存储的状态数据颗粒度一样吗?
  2. 它们把状态数据存储到哪里去了?
  3. 如果系统后台将Activity杀掉后,它们都能把状态恢复回来吗?
  1. onSaveInstanceState(Bundle outState)方法详解

首先在ComponentActivityonSaveInstanceState(Bundle outState) 方法中加个断点,重点关注 ActivityThread.callActivityOnSaveInstanceState(Bundle outState)。

2.1 ActivityThread.callActivityOnSaveInstanceState(Bundle outState)

1
2
3
4
5
6
7
8
9
10
11
12
13
//ActivityThread.java

private void callActivityOnSaveInstanceState(ActivityClientRecord r) {
    r.state = new Bundle();
    r.state.setAllowFds(false);
    if (r.isPersistable()) {
        r.persistentState = new PersistableBundle();
        mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
                r.persistentState);
    } else {
        mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
    }
}

我们注意到r.state = new Bundle(), 原来outState参数是在这里创建的。Bundle可以用来组件间传递数据,也可以用来进程间传递数据。

2.2 ActivityThread.handleStopActivity()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void handleStopActivity(IBinder token, boolean show, int configChanges,
        PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) {
    final ActivityClientRecord r = mActivities.get(token);
    r.activity.mConfigChangeFlags |= configChanges;

    final StopInfo stopInfo = new StopInfo();
    performStopActivityInner(r, stopInfo, show, true /* saveState */, finalStateRequest,
            reason);
    updateVisibility(r, show);

    // Make sure any pending writes are now committed.
    if (!r.isPreHoneycomb()) {
        QueuedWork.waitToFinish();
    }

    stopInfo.setActivity(r);
    stopInfo.setState(r.state);
    stopInfo.setPersistentState(r.persistentState);
    pendingActions.setStopInfo(stopInfo);
    mSomeActivitiesChanged = true;
}

2.3 PendingTransactionActions$StopInfo.run()

该方法调用了 ActivityManager.getService().activityStopped(mActivity.token, mState, mPersistentState, mDescription) 方法,还将 2.1 中创建的 mState 当参数传进来了。

2.4 ActivityManagerService.activityStopped()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//ActivityManagerService.java
@Override
public final void activityStopped(IBinder token, Bundle icicle,
        PersistableBundle persistentState, CharSequence description) {
    // Refuse possible leaked file descriptors
    if (icicle != null && icicle.hasFileDescriptors()) {
        throw new IllegalArgumentException("File descriptors passed in Bundle");
    }
    final long origId = Binder.clearCallingIdentity();
    synchronized (this) {
        final ActivityRecord r = ActivityRecord.isInStackLocked(token);
        if (r != null) {
            r.activityStoppedLocked(icicle, persistentState, description);
        }
    }
    trimApplications();
    Binder.restoreCallingIdentity(origId);
}

2.5 ActivityRecord.activityStoppedLocked

我们注意到最终bundle数据会保存在ActivityRecordicicle对象中。

总结onSaveInstanceState方法是当Activity调用了onStop后,会调用到ActivityThreadcallActivityOnSaveInstanceState()方法,把Activity需要保存的数据放入Bundle对象中,并且随后通过IPC进程间通信机制,调用ActivityManagerService的activityStopped方法,将Bundle对象保存到AMS端的ActivityRecord中。

2.6 被杀端后恢复数据过程

最终是通过 ActivityStackSupervisor.realStartActivityLocked → LaunchActivityItem.execute() → ActivityThread.performLaunchActivity 用 r.state 恢复数据:

1
2
3
4
5
6
7
8
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent){
   activity.mCalled = false;
    if (r.isPersistable()) {
        mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    } else {
        mInstrumentation.callActivityOnCreate(activity, r.state);
    }
}
  1. onRetainNonConfigurationInstance()

该方法是在重建Activity时调用performDestoryActivity时会保存数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
          int configChanges, boolean getNonConfigInstance, String reason) {
  ActivityClientRecord r = mActivities.get(token);
  //省略一些代码
      if (getNonConfigInstance) {
          try {
              r.lastNonConfigurationInstances
                      = r.activity.retainNonConfigurationInstances();
          } catch (Exception e) {
              // ...
        }
      }
  }

我们可以看到onRetainNonConfigurationInstance方法返回的Object会赋值给ActivityClientRecordlastNonConfigurationInstances

  1. 答案

  2. 颗粒度不一样。onSaveInstanceState()是保存到Bundle中,只能保存Bundle能接受的数据类型,比如一些基本类型的数据。而onRetainNonConfigurationInstance() 可以保存任何类型的数据,数据类型是Object
  3. onSaveInstanceState()数据最终存储到ActivityManagerServiceActivityRecord中了,也就是存到系统进程中去了。而onRetainNonConfigurationInstance() 数据是存储到ActivityClientRecord中,也就是存到应用本身的进程中了
  4. onSaveInstanceState存到系统进程中,所以App被杀之后还是能恢复的。而onRetainNonConfigurationInstance存到本身进程中,App被杀是没法恢复的。
本文由作者按照 CC BY 4.0 进行授权