Activity异常关闭时数据的存储和恢复过程分析

 

在正常情况下关闭Activity,例如按下返回键、返回箭头等Activity直接就被销毁。但是在Activity内存不足、异常关闭或者屏幕旋转等情况的时候,当前的Activity会被销毁,这个过程中页面上的Fragment、View等信息会被保存下来,当再次展示的时候会被重建,重建后的Activity就会恢复这些数据。一般情况下当系统配置发生改变的时候Activity都会被重建,如果我们不希望当系统配置发生变化界面重建,那么我们需要在AndroidManifest.xml中对Activity的configChange属性进行配置。如下:

    android:configChanges="orientation"

当有多个属性时,用|进行分隔。这种情况下这里就不进行讨论,这里主要讨论当没有配置configChanges时候,Activity异常销毁和重建后是如何保存数据和恢复数据的。

一、onSaveInstanceState保存数据入口分析

当Activity异常关闭时候,会回调onSaveInstanceState方法,onSaveInstanceState的调用是处于onPause和onStop之间的,能保证的是onSaveInstanceState方法会在onStop之前调用,但是是否在onPause之前就不一定了。

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
    }

在Activity的源码中主要做了三件事情,如下源码标注:

  1. 存储当前窗口的视图树状态;
  2. 存储fragment状态;
  3. 存储生命周期状态。
    protected void onSaveInstanceState(Bundle outState) {
        //存储当前窗口的视图树状态
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());

        outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
        //存储fragment状态
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
        if (mAutoFillResetNeeded) {
            outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
            getAutofillManager().onSaveInstanceState(outState);
        }
        //设置生命周期状态
        getApplication().dispatchActivitySaveInstanceState(this, outState);
    }

二、 存储当前窗口的视图树状态

首先来看如何保持当前的View视图:

outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());

从上面的源码中可以看出具体的存储是在mWindow中的,而mWindow的实现类是PhoneWindow,因此找到PhoneWindow对应的方法,saveHierarchyState主要做了如下几件事:

  1. 保存根View;
  2. 保存当前页面含有焦点的View;
  3. 保存整个面板内容;
  4. 保存actionBar信息。
    @Override
    public Bundle saveHierarchyState() {
        Bundle outState = new Bundle();
        if (mContentParent == null) {
            return outState;
        }
        //1.保存根View
        //通过SparseArray来存储数据,太相当于一个key为Integer类型的Map集合
        SparseArray<Parcelable> states = new SparseArray<Parcelable>();
        //调用mContentParent的saveHierarchyState方法,mContentParent为ViewGroup,具体的实现在父View中,而不是在ViewGroup
        mContentParent.saveHierarchyState(states);
        //将视图树放置到Bundle
        outState.putSparseParcelableArray(VIEWS_TAG, states);

        //2.保存当前页面含有焦点的View
        final View focusedView = mContentParent.findFocus();
        //含有焦点的视图必须设置有id否则无法恢复他的焦点状态
        if (focusedView != null && focusedView.getId() != View.NO_ID) {
            outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
        }

        //3.保存整个面板内容
        SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>();
        savePanelState(panelStates);
        if (panelStates.size() > 0) {
            outState.putSparseParcelableArray(PANELS_TAG, panelStates);
        }

        //4.保存actionBar信息
        if (mDecorContentParent != null) {
            SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>();
            mDecorContentParent.saveToolbarHierarchyState(actionBarStates);
            outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
        }

        return outState;
    }

这里保存View通过SparseArray来存储数据,太相当于一个key为Integer类型的Map集合,不难发现这里一共创建了states、panelStates、actionBarStates三个集合分别来存储数据。同时可以看出前页面含有焦点的View没有设置id,则不会被保存。这里重点分析如何保存整个View,在上面源码中:

     mContentParent.saveHierarchyState(states);

mContentParent是一个ViewGroup,而ViewGroup中并没该方法,因此可以断定saveHierarchyState方法是其父类View的方法:

    public void saveHierarchyState(SparseArray<Parcelable> container) {
        dispatchSaveInstanceState(container);
    }
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
        //1、如果当前的View没有设置id则该View的状态不会被存储
        if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
            mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
            //2、获取自身状态(默认为空)
            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);
                //3、将自身状态存放到SparseArray里面,key为id,vaule为自身View的状态
                container.put(mID, state);
            }
        }
    }

首先判断如果当前的View没有设置id则该View的状态不会被存储,然后获取自身的Parcelable状态(默认为空),最后将该status保存下来。其实ViewGroup重写了dispatchSaveInstanceState方法,主要是保存自身状态和遍历View保存子View状态。

    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
        //1.保存自身状态
        super.dispatchSaveInstanceState(container);
        final int count = mChildrenCount;
        final android.view.View[] children = mChildren;
        //2.遍历View保存子View状态
        for (int i = 0; i < count; i++) {
            android.view.View c = children[i];
            if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
                c.dispatchSaveInstanceState(container);
            }
        }
    }

上面说过获取自身的Parcelable状态(默认为空),这里重点看看如何获取View自身的状态值,看onSaveInstanceState方法:

    protected Parcelable onSaveInstanceState() {
        mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
        ……………………………………………省略若果代码…………………………………………………………………
        return BaseSavedState.EMPTY_STATE;
    }

这个是获取当前View的默认状态,默认是Empty,在开发中,我们需要存储View的默认状态时候,就需要重写该方法,以TextView为例:来看看TextView中如何重写的:

    @Override
    public Parcelable onSaveInstanceState() {
        //获取空的superState
        Parcelable superState = super.onSaveInstanceState();

        // Save state if we are forced to
        final boolean freezesText = getFreezesText();
        boolean hasSelection = false;
        int start = -1;
        int end = -1;

        //保存start和end的文本信息
        if (mText != null) {
            start = getSelectionStart();
            end = getSelectionEnd();
            if (start >= 0 || end >= 0) {
                // Or save state if there is a selection
                hasSelection = true;
            }
        }

        if (freezesText || hasSelection) {
            SavedState ss = new SavedState(superState);

            if (freezesText) {
                if (mText instanceof Spanned) {
                    final Spannable sp = new SpannableStringBuilder(mText);

                    if (mEditor != null) {
                        removeMisspelledSpans(sp);
                        sp.removeSpan(mEditor.mSuggestionRangeSpan);
                    }

                    ss.text = sp;
                } else {
                    ss.text = mText.toString();
                }
            }

            if (hasSelection) {
                // XXX Should also save the current scroll position!
                ss.selStart = start;
                ss.selEnd = end;
            }

            if (isFocused() && start >= 0 && end >= 0) {
                ss.frozenWithFocus = true;
            }

            ss.error = getError();

            if (mEditor != null) {
                ss.editorState = mEditor.saveInstanceState();
            }
            return ss;
        }
        //返回最终信息
        return superState;
    }

重写完该方法后就会通过dispatchSaveInstanceState中的 container.put(mID, state);添加到集合里面,完成了View状态的存储。

三、存储fragment状态

    Parcelable p = mFragments.saveAllState();

在Fragment中onSaveInstanceState是一个空实现。

    public Parcelable saveAllState() {
        return mHost.mFragmentManager.saveAllState();
   }

FragmentManager.saveAllState()才是真正的核心部分。主要分为 3 部分:

  1. 保存当前管理器Fragment到FragmentState;
  2. 得到 mAdded 中 Fragment 的 index 列表;
  3. 保存已经mAdded的Fragment到新的栈内。
    Parcelable saveAllState() {
	   //释放资源
	   forcePostponedTransactions();
	   endAnimatingAwayFragments();
	   execPendingActions();

	   mStateSaved = true;
	   mSavedNonConfig = null;

	   if (mActive == null || mActive.size() <= 0) {
		   return null;
	   }

	   //1.保存当前管理器Fragment到FragmentState中
	   int N = mActive.size();
	   FragmentState[] active = new FragmentState[N];
	   boolean haveFragments = false;
	   for (int i=0; i<N; i++) {
		   Fragment f = mActive.valueAt(i);
		   if (f != null) {
			   //如果 f.mIndex<0,则Fragment没有在栈内,无法被保存
			   if (f.mIndex < 0) {
				   throwException(new IllegalStateException(
						   "Failure saving state: active " + f
						   + " has cleared index: " + f.mIndex));
			   }

			   haveFragments = true;
			   //例化一个 FragmentState 并把一些基础属性赋值。
			   FragmentState fs = new FragmentState(f);
			   active[i] = fs;

			   if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
				   //在该方法中首先触发 onSaveInstanceState 可以让开发者保存一些自定义的属性到 Bundle 中,然
				   //后调用 saveHierarchyState 方法保存 Fragment 内 View 的状态到 Bundle 中
				   fs.mSavedFragmentState = saveFragmentBasicState(f);
				   //如果TAG为空,则设置tag
				   if (f.mTarget != null) {
					   if (f.mTarget.mIndex < 0) {
						   throwException(new IllegalStateException(
								   "Failure saving state: " + f
								   + " has target not in fragment manager: " + f.mTarget));
					   }
					   if (fs.mSavedFragmentState == null) {
						   fs.mSavedFragmentState = new Bundle();
					   }
					   //保存tag
					   putFragment(fs.mSavedFragmentState,
							   FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
					   if (f.mTargetRequestCode != 0) {
					   //保存RequestCode
						   fs.mSavedFragmentState.putInt(
								   FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
								   f.mTargetRequestCode);
					   }
				   }

			   } else {
				   //否则直接设置把Fragment数据设置到FragmentState
				   fs.mSavedFragmentState = f.mSavedFragmentState;
			   }

			   if (DEBUG) Log.v(TAG, "Saved state of " + f + ": "
					   + fs.mSavedFragmentState);
		   }
	   }

	   if (!haveFragments) {
		   if (DEBUG) Log.v(TAG, "saveAllState: no fragments!");
		   return null;
	   }

	   int[] added = null;
	   BackStackState[] backStack = null;

	   //2.得到 mAdded 中 Fragment 的 index 列表
	   N = mAdded.size();
	   if (N > 0) {
		   added = new int[N];
		   for (int i = 0; i < N; i++) {
			   added[i] = mAdded.get(i).mIndex;
			   if (added[i] < 0) {
				   throwException(new IllegalStateException(
						   "Failure saving state: active " + mAdded.get(i)
						   + " has cleared index: " + added[i]));
			   }
			   if (DEBUG) {
				   Log.v(TAG, "saveAllState: adding fragment #" + i
						   + ": " + mAdded.get(i));
			   }
		   }
	   }

	   //3.保存已经mAdded的Fragment到新的栈内
	   if (mBackStack != null) {
		   N = mBackStack.size();
		   if (N > 0) {
			   backStack = new BackStackState[N];
			   for (int i=0; i<N; i++) {
				   backStack[i] = new BackStackState(mBackStack.get(i));
				   if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
						   + ": " + mBackStack.get(i));
			   }
		   }
	   }

	   FragmentManagerState fms = new FragmentManagerState();
	   fms.mActive = active;
	   fms.mAdded = added;
	   fms.mBackStack = backStack;
	   if (mPrimaryNav != null) {
		   fms.mPrimaryNavActiveIndex = mPrimaryNav.mIndex;
	   }
	   fms.mNextFragmentIndex = mNextFragmentIndex;
	   //保存其他额外信息
	   saveNonConfig();
	   return fms;
   }

四、存储生命周期状态

Activity:

    getApplication().dispatchActivitySaveInstanceState(this, outState);

Application:

    void dispatchActivitySaveInstanceState(Activity activity, Bundle outState) {
		//获取回调信息数组
		Object[] callbacks = collectActivityLifecycleCallbacks();
		//遍历回调数组,
		if (callbacks != null) {
			for (int i=0; i<callbacks.length; i++) {
				//保存把outState保存到onActivitySaveInstanceState,他是ActivityLifecycleCallbacks接口的一个方法
				//包括很多实现类EmptyActivityLifecycleCallbacks、NfcActivityManager等
				((ActivityLifecycleCallbacks)callbacks[i]).onActivitySaveInstanceState(activity,
						outState);
			}
		}
	}

主要完成以下:

  1. 获取回调信息数组集;
  2. 遍历回调数组,设置生命周期回调。

五、Activity异常关闭时数据的存储总结

上面主要分析了Activity异常关闭时数据的存储的三大部分:

  • 存储当前窗口的视图树状态;
  • 存储fragment状态;
  • 存储生命周期状态。

现在对这三大部分进行总结,绘制了一张图,如下:

六、Activity异常关闭时数据的恢复过程

通过上面的分析得知Activity异常关闭时信息保存在bundle里面,哪么bundle数据有存在那块呢?重建Activity是如何恢复的呢?上面说过调用onSaveInstanceState方法是在onStop方法之前,Activity的onStop方法是在ActivityThread的performStopActivity,省略部分代码,如下:

    final void performStopActivity(IBinder token, boolean saveState, String reason) {
        //通过Token获取ActivityClientRecord对象,存储了Activity信息
        ActivityClientRecord r = mActivities.get(token);
        //saveState是否要存储的状态
        performStopActivityInner(r, null, false, saveState, reason);
    }
    private void performStopActivityInner(ActivityClientRecord r,
            StopInfo info, boolean keepShown, boolean saveState, String reason) {

        if (r != null) {
            //判断是否要保存Activity的状态
            if (!r.activity.mFinished && saveState) {
                if (r.state == null) {
                    //执行Activity中的onSaveInstanceState
                    callCallActivityOnSaveInstanceState(r);
                }
            }

            if (!keepShown) {
                try {
                    //执行Activity中的onStop
                    r.activity.performStop(false /*preserveWindow*/);
                } catch (Exception e) {

                }
        }
    }
    private void callCallActivityOnSaveInstanceState(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 {
            //实际上会调用Activity中的onSaveInstanceState
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
        }
    }

在通过performStopActivity中通过Token获取ActivityClientRecord对象,存储了Activity信息,之后调用了callCallActivityOnSaveInstanceState方法.再启动Activity的时候再将bundle传递到Activity的onCreate方法。如下:

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        /………………………………………………………………省略代码…………………………………………………………………………/
        //传递给Activity的onCreate方法
        if (r.isPersistable()) {
            mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
        } else {
            mInstrumentation.callActivityOnCreate(activity, r.state);
        }
        /………………………………………………………………省略代码…………………………………………………………………………/
       //传递给Activity的onRestoreInstanceState方法
        if (!r.activity.mFinished) {
            if (r.isPersistable()) {
                if (r.state != null || r.persistentState != null) {
                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                            r.persistentState);
                }
            } else if (r.state != null) {
                mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
            }
         }
         /………………………………………………………………省略代码…………………………………………………………………………/
    }

这样Activity重启的时候就能够读取到保存的信息了,很好的提升了用户体验。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值