扒一扒场景恢复的代码

2019-03-26  本文已影响0人  HuBoZzz

今天扒一扒场景恢复的代码;

流程顺序

大致看下调用方式,没有按照严格的时序图方式写!


调用顺序

ActivityThread

-> ActivityThread.callActivityOnSaveInstanceState(ActivityClientRecord r)
此方法有三个调用:分别对应三个调用分支

    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<Parcelable> states = new SparseArray<Parcelable>();//这个证明所有的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<Parcelable> panelStates = new SparseArray<Parcelable>();
        savePanelState(panelStates);
        if (panelStates.size() > 0) {
            outState.putSparseParcelableArray(PANELS_TAG, panelStates);
        }

        if (mDecorContentParent != null) {
            SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>();
            mDecorContentParent.saveToolbarHierarchyState(actionBarStates);
            outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
        }

        return outState;
    }

继续看mContentParent.saveHierarchyState(states);干了些什么?
找了一下ViewGroup没找到,那么就去View里面找,找到了
View#saveHierarchyState

 public void saveHierarchyState(SparseArray<Parcelable> container) {
        dispatchSaveInstanceState(container);
    }

然后去找 ViewGroup#dispatchSaveInstanceState

 @Override
    protected void dispatchSaveInstanceState(SparseArray<Parcelable> 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<Parcelable> 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<IBinder, ActivityClientRecord> 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<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();所以当会销毁Activity重建的时候自然可以从此集合里再次获取数据!

恢复的代码和保存的代码调用几乎一致,所以就不写了!

此处应有签名
上一篇下一篇

猜你喜欢

热点阅读