今天扒一扒场景恢复的代码;
流程顺序
大致看下调用方式,没有按照严格的时序图方式写!
ActivityThread
-> ActivityThread.callActivityOnSaveInstanceState(ActivityClientRecord r)
此方法有三个调用:分别对应三个调用分支
- 分支一: Honeycomb之前(Api<11(Android3.0.x)),调用在pausing之前
- 分支二: P之前(api<28(Android9.0))被调用在onstop之前,
- 分之三: P开始在onstop之后
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);
}
}
if (r.isPersistable())注意这里方法,这里决定是否走持久化方法
需在Manifest中的activity设置属性:
android:persistableMode="persistAcrossReboots"
这可不是我说的,我看这位小伙子的,我没论证persistableMode与Activity的持久化
->
调用了Instrumentation#callActivityOnSaveInstanceState(Activity activity, Bundle outState,
PersistableBundle outPersistentState)
activity.performSaveInstanceState(outState, outPersistentState);
->
Activity#performSaveInstanceState(Bundle outState, PersistableBundle outPersistentState)
final void performSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
onSaveInstanceState(outState, outPersistentState);
saveManagedDialogs(outState);
storeHasCurrentPermissionRequest(outState);
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState +
", " + outPersistentState);
}
这里执行了Activity的onSaveInstanceState(outState, outPersistentState)方法.
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
onSaveInstanceState(outState);
}
注意这里直接调用了默认的onSaveInstanceState()一个参数的,而持久化参数的使用,需要我们在Activity里面重载这个方法,然后我们把咱们要保存的数据穿到这个对象里面就好了.
Activity#onSaveInstanceState(Bundle outState)
protected void onSaveInstanceState(Bundle outState) {
//这里获取整个window的需要保存的数据
outState.putBundle(WINDOW_HIERARCHY_TAG,mWindow.saveHierarchyState());
outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
Parcelable p = mFragments.saveAllState();//这里是保存Fragment的数据
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
if (mAutoFillResetNeeded) {
outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
getAutofillManager().onSaveInstanceState(outState);
}
getApplication().dispatchActivitySaveInstanceState(this, outState);//这里是给ActivityLifecycleCallbacks#onActivitySaveInstanceState的回调
}
这里就是默认的实现方法,保存了系统的一些对象,然后你重载这个就可以保存自己的了.
我们去验证一下View数据的保存;
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
PhoneWindow实现了Window,所以去PhoneWindow去找saveHierarchyState();
public Bundle saveHierarchyState() {
Bundle outState = new Bundle();
if (mContentParent == null) {
return outState;
}
//主要看着里
SparseArray states = new SparseArray();//这个证明所有的View都存放在同一个集合里
mContentParent.saveHierarchyState(states);//开始ViewGroup的遍历赋值
outState.putSparseParcelableArray(VIEWS_TAG, states);//这是放到了Bundle里
// Save the focused view ID.//需要获取焦点的id
final View focusedView = mContentParent.findFocus();
if (focusedView != null && focusedView.getId() != View.NO_ID) {
outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
}
// save the panels
SparseArray panelStates = new SparseArray();
savePanelState(panelStates);
if (panelStates.size() > 0) {
outState.putSparseParcelableArray(PANELS_TAG, panelStates);
}
if (mDecorContentParent != null) {
SparseArray actionBarStates = new SparseArray();
mDecorContentParent.saveToolbarHierarchyState(actionBarStates);
outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
}
return outState;
}
继续看mContentParent.saveHierarchyState(states);干了些什么?
找了一下ViewGroup没找到,那么就去View里面找,找到了
View#saveHierarchyState
public void saveHierarchyState(SparseArray container) {
dispatchSaveInstanceState(container);
}
然后去找 ViewGroup#dispatchSaveInstanceState
@Override
protected void dispatchSaveInstanceState(SparseArray container) {
super.dispatchSaveInstanceState(container);
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
View c = children[i];
if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
c.dispatchSaveInstanceState(container);
}
}
}
上面就是递归调用了
下面就是基线条件了;
View#dispatchSaveInstanceState()
protected void dispatchSaveInstanceState(SparseArray container) {
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
Parcelable state = onSaveInstanceState();
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onSaveInstanceState()");
}
if (state != null) {
// Log.i("View", "Freezing #" + Integer.toHexString(mID)
// + ": " + state);
container.put(mID, state);
}
}
}
最后我们拿到了所有有id的并且需要存储数据View的数据;也就是id不能重复,重复了数据就会被最后一个id覆盖。
敲重点:
通过递归dispatchSaveInstanceState,来调用View的onSaveInstanceState(),最终拿到所有数据,值得注意的是View的id不能重复,重复最后的id会覆盖前一个id,所以在Include布局的时候,自定义ViewGroup的时候都会出现重复id的问题,例如下面这个问题LottieAnimationView场景恢复-导致的底部按钮显示相同
看到这里,我突然想起来,没看到Bundle的存储呀,往上面找了找,就看到从Activity的值被赋值到了callActivityOnSaveInstanceState(ActivityClientRecord r)里,我们再看下这段代码
Activity#callActivityOnSaveInstanceState
private void callActivityOnSaveInstanceState(ActivityClientRecord r) {
r.state = new Bundle();//在这里new了出来,下面的都是赋值逻辑
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);
}
}
那我们再看看ActivityClientRecord,先看看这个ActivityClientRecord是从哪来的,
ActivityThread#performPauseActivity
private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason,
PendingTransactionActions pendingActions) {
// Pre-Honeycomb apps always save their state before pausing
final boolean shouldSaveState = !r.activity.mFinished && r.isPreHoneycomb();
if (shouldSaveState) {
//这个是咱们一直在看的那个save方法
callActivityOnSaveInstanceState(r);
}
这里没找到,我们再往上找
ActivityThread#handlePauseActivity
@Override
public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
int configChanges, PendingTransactionActions pendingActions, String reason) {
//就在这
ActivityClientRecord r = mActivities.get(token);
if (r != null) {
//***
r.activity.mConfigChangeFlags |= configChanges;
//这个使我们刚才跟的方法
performPauseActivity(r, finished, reason, pendingActions);
//....
}
}
好的我们知道了怎么获得ActivityClientRecord的了
妙啊!原来每个Activity的数据都给放在这个集合里了
final ArrayMap mActivities== new ArrayMap<>();
在哪被put呢?
ActivityThread#performLaunchActivity
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//...
mActivities.put(r.token, r);
//...
return activity;
}
晓得了在哪put了,那哪里创建的ActivityClientRecord呢?
ActivityThread#startActivityNow
public final Activity startActivityNow(Activity parent, String id,
Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
Activity.NonConfigurationInstances lastNonConfigurationInstances) {
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = 0;
r.intent = intent;
r.state = state;
r.parent = parent;
r.embeddedID = id;
r.activityInfo = activityInfo;
r.lastNonConfigurationInstances = lastNonConfigurationInstances;
//...
return performLaunchActivity(r, null /* customIntent */);
}
舒服了,找到了handleLaunchActivity!我觉得我们可以到此收手了,因为中间要经过Activity启动流程,因为不是咱们说的重点,我们不看这个,如果想看,可以看下这个Android 7.0 startActivity()源码解析以及对几个问题的思考
这里可以看到每个Activity创建的时候会创建一个ActivityClientRecord用来保存数据,并且存在集合里面。
好了,敲黑板!重点来了
我们可以知道ActivityClientRecord是记录Activity的一些属性的。当页面销毁的时候,由于把信息存到了全局的 final ArrayMap
mActivities = new ArrayMap<>();所以当会销毁Activity重建的时候自然可以从此集合里再次获取数据!
恢复的代码和保存的代码调用几乎一致,所以就不写了!