美文网首页
扒一扒场景恢复的代码

扒一扒场景恢复的代码

作者: HuBoZzz | 来源:发表于2019-03-26 13:18 被阅读0次

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

    流程顺序

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


    调用顺序

    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<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重建的时候自然可以从此集合里再次获取数据!

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

    此处应有签名

    相关文章

      网友评论

          本文标题:扒一扒场景恢复的代码

          本文链接:https://www.haomeiwen.com/subject/hxsgvqtx.html