聊聊Activity的生命周期,onSaveInstanceState/onRestoreInstanceState源码分析

虽然我这博客也没几个人看,但还是要坚持写下去,域名也买了,所以今年目标的其中一个就是做好我的个人页面,近期先链接到Csdn这里,www.tiandroid.com这就是我未来的个人主页,敬请期待。。。

今天呢就写点关于Activity的简单的东西,生命周期和启动模式。关于Activity比较复杂的便是启动流程,会在以后慢慢更新,也许是下周,也许是。。。
实际上想起写这东西是因为偶然看到了一个面经

E:“onSaveInstanceState什么时候调用?”
m:“按Home键。。。或者。。。”
E:“按Home键就要调用onSaveInstanceState,那岂不是应用只要返回前台就要调用onRestoreInstanceState?这么麻烦吗”
m:“。。。”

这两个东西到底什么时候调用呢?是不是save了就要restore呢?他们和生命周期有什么关系?

不再扯废话了,开始第一部分:

生命周期

生命周期就和人的生命一样,可以分为健康的时候和生病的时候,对应着

  • 典型情况:即用户参与下的生命周期的改变
  • 异常情况:①系统回收Activity ②设备的Configuration改变导致Activity销毁重建

首先说典型情况,

典型

那这里免不了放一张官方的图
这里写图片描述
这个图我就不多解释了,只说一些注意情况

  • onCreate中加入finish(),则不会调用onStart:
    onCreate→onDestroy
  • onCreate中加入super.onStop/onDestroy,则会调用onStart:
    onCreate→onStop/onDestroy→onStart→onResume
  • 新Activity如果是透明的,那么之前的Activity只会调用onPause,不会onStop的
    这里可以多说一句,onStart和OnResume,onPause和onStop之间的区别只是可见与否
  • 用户打开新Activity时,先调用老Activity的onPause,再调用新Activity的onResume
    这一条有个隐身含义就是onPause最好不要做耗时的操作,否则会影响新页面的切换。

正常情况下的特例或者是需要特殊记得点就这么多,如果还有特殊的请留言。
那么接下来我们看一下异常情况下的生命周期

异常

异常下无论哪种都会经历以下流程

Created with Raphaël 2.1.2 onPause onSaveInstanceState 二者没有固定先后执行顺序 onStop onDestroy onCreate onStart onRestoreInstanceState

有异常状况,Activity销毁,重建,并对应着onSaveInstanceState,onRestoreInstanceState来储存和恢复数据。
这个异常状况分两种
第一种情况,内存不足时候不出众的Activity就被干死了。。。

以“出众”作为评价标准大致分为三种:

  • 前台,甭说了,就是与用户交互的Activity,正室,优先级最高
  • 可见但又不是前台,比如弹了个窗,他后面的Activity,这就是侧室,优先级低了那么一点
  • 不可见的Acitivty,冷宫,这就是最先被杀的那个

第二种情况,资源相关的系统配置变了
比如我旋转了屏幕,竖屏变成了横屏,配置变了,就只能按上面的图重新来过。

过程不难,但是重点是提到的两个回调方法onSaveInstanceState,onRestoreInstanceState,下面就从源码来看到底里面做了什么操作

源码分析

先看一段介绍

     * <p>This method is called before an activity may be killed so that when it
     * comes back some time in the future it can restore its state.  For example,
     * if activity B is launched in front of activity A, and at some point activity
     * A is killed to reclaim resources, activity A will have a chance to save the
     * current state of its user interface via this method so that when the user
     * returns to activity A, the state of the user interface can be restored
     * via {@link #onCreate} or {@link #onRestoreInstanceState}.


     * <p>If called, this method will occur before {@link #onStop}.  There are
     * no guarantees about whether it will occur before or after {@link #onPause}.

就是说onSaveInstanceState方法会在Activity被kill之前执行。一定是在onStop之前,不确定在onPause前或后。

来看源码onSaveInstanceState(Bundle outState)

    protected void onSaveInstanceState(Bundle outState) {
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());

        outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
        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);
    }

分两步走

  1. 存了下对应的Window的State信息,放到outState中
  2. 又存了下所有Fragements的信息,也存放在outState中了

这两个都是通过保存根View的状态来实现的,所以我们从mWindow.saveHierarchyState()开始看起,Window的唯一实现类是PhoneWindow(我怎么知道的?是在看事件分发机制的时候,事件分发也挺有意思,有空写写),所以就去PhoneWindow里找到

    public Bundle saveHierarchyState() {
        Bundle outState = new Bundle();
        if (mContentParent == null) {
            return outState;
        }

        SparseArray<Parcelable> states = new SparseArray<Parcelable>();
        mContentParent.saveHierarchyState(states);
        outState.putSparseParcelableArray(VIEWS_TAG, states);

        // Save the focused view 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;
    }

来起来比较多,但实际就是

  1. 存了下View信息
  2. 存了下View焦点信息
  3. 存了下抽屉控件信息
  4. 又存了下ActionBar信息
    把存好的Bundle返回去了,在第一步中注意到有一个同名的方法mContentParent.saveHierarchyState(states);点进去看一下
    public void saveHierarchyState(SparseArray<Parcelable> container) {
        dispatchSaveInstanceState(container);
    }
    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);
            }
        }
    }

实际上调用的是dispatchSaveInstanceState,开头可以看到他只存了有Id的,还是很聪明的,我们还是看重点,其中核心为onSaveInstanceState();

    protected Parcelable onSaveInstanceState() {
        mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
        if (mStartActivityRequestWho != null) {
            BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE);
            state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
            return state;
        }
        return BaseSavedState.EMPTY_STATE;
    }

这个方法检查了一下mStartActivityRequestWho这个String对象是否为null,mStartActivityRequestWho这个对象只有在调用了View#startActivityForResult时才会设置,这时标记一下state中的状态,为null时,直接返回一个空状态。
总结一下,saveHierachyState()方法直接传递给了dispatchSaveInstanceState()方法。dispatchSaveInstanceState()是分发保存状态这个事件,它默认是在有ID的情况下保存自身的state,没有id就拉倒。上面看的是View类的方法,实际mContentParent是个ViewGroup,我们知道ViewGroup是View的子类,所以ViewGroup和其他控件类可以重写这个方法,将保存状态的行为分发到子类中。onSaveInstanceState()方法用来具体地保存当前View的状态。所以看一下ViewGroup的分发

    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()保存自己,然后遍历子View,调用对应的dispatchSaveInstanceState()方法,然后通过各个控件的重写onSaveInstanceState保存。下面举个例子,看一下TextView#onSaveInstanceState()是怎么保存状态的:

public Parcelable onSaveInstanceState() {
    Parcelable superState = super.onSaveInstanceState();

    // Save state if we are forced to
    boolean save = mFreezesText;
    int start = 0;
    int end = 0;

    if (mText != null) {
        start = getSelectionStart();
        end = getSelectionEnd();
        if (start >= 0 || end >= 0) {
            // Or save state if there is a selection
            save = true;
        }
    }

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

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

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

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

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

        ss.error = getError();

        if (mEditor != null) {
            ss.editorState = mEditor.saveInstanceState();
        }
        return ss;
    }

    return superState;
}

从上述源码中很容看出他保存了文本的选中状态和文本的内容,end。

总结一下吧:
1.在Activity被回收时,会触发一个onSaveInstanceState的事件;
2.分发事件,onSaveInstanceState事件从Activity->Window(PhoneWindow)->顶级ViewGroup(一般是DecorView)->一一通知子元素保存;
3.以View的ID作为key存起来。

会看整个内容,有很多关键词在Android中有很多的应用,比如:dispatch、PhoneWindow、DecorView等等,可以联系起很多具有这种分发委托思想的结构,像事件分发、View绘制等等都是类似的,所以学好学透一个会对以后学习其他的有很大帮助。

onRestoreInstanceState

回到最开始的问题,因为onSaveInstanceState是在Activity销毁之前调用,任何会被认为将要销毁的操作都可以触发onSaveInstanceState,情况还不少呢,那每次save都会调用onRestoreInstanceState,这么麻烦的吗?

当然不会,虽然他俩名字很像,但其实并不是配套出现的

  • onSaveInstanceState会在将要销毁时候调用
  • onRestoreInstanceState会在销毁之后重启时调用
    也就是说真的kill了才调onRestoreInstanceState
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        if (mWindow != null) {
            Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
            if (windowState != null) {
                mWindow.restoreHierarchyState(windowState);
            }
        }
    }

它的原理就是把存了的数据拿出来,调用PhoneWindow的restoreHierarchyState(windowState)

    public void restoreHierarchyState(Bundle savedInstanceState) {
        if (mContentParent == null) {
            return;
        }

        SparseArray<Parcelable> savedStates
                = savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
        if (savedStates != null) {
            mContentParent.restoreHierarchyState(savedStates);
        }

        // restore the focused view
        int focusedViewId = savedInstanceState.getInt(FOCUSED_ID_TAG, View.NO_ID);
        if (focusedViewId != View.NO_ID) {
            View needsFocus = mContentParent.findViewById(focusedViewId);
            if (needsFocus != null) {
                needsFocus.requestFocus();
            } else {
                Log.w(TAG,
                        "Previously focused view reported id " + focusedViewId
                                + " during save, but can't be found during restore.");
            }
        }

        // Restore the panels.
        SparseArray<Parcelable> panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG);
        if (panelStates != null) {
            restorePanelState(panelStates);
        }

        if (mDecorContentParent != null) {
            SparseArray<Parcelable> actionBarStates =
                    savedInstanceState.getSparseParcelableArray(ACTION_BAR_TAG);
            if (actionBarStates != null) {
                doPendingInvalidatePanelMenu();
                mDecorContentParent.restoreToolbarHierarchyState(actionBarStates);
            } else {
                Log.w(TAG, "Missing saved instance states for action bar views! " +
                        "State will not be restored.");
            }
        }
    }

就不多说了,和保存完全一样的,和之前一样还是看一下restoreHierarchyState(savedStates)

    public void restoreHierarchyState(SparseArray<Parcelable> container) {
        dispatchRestoreInstanceState(container);
    }

    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
        if (mID != NO_ID) {
            Parcelable state = container.get(mID);
            if (state != null) {
                // Log.i("View", "Restoreing #" + Integer.toHexString(mID)
                // + ": " + state);
                mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
                onRestoreInstanceState(state);
                if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
                    throw new IllegalStateException(
                            "Derived class did not call super.onRestoreInstanceState()");
                }
            }
        }
    }

实际上调用的是dispatchRestoreInstanceState,通过ID去拿数据,其中核心为onRestoreInstanceState();

    protected void onRestoreInstanceState(Parcelable state) {
        mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
        if (state != null && !(state instanceof AbsSavedState)) {
            throw new IllegalArgumentException("Wrong state class, expecting View State ");
        }
        if (state != null && state instanceof BaseSavedState) {
            mStartActivityRequestWho = ((BaseSavedState) state).mStartActivityRequestWhoSaved;
        }
    }

就是直接拿到mStartActivityRequestWho,就和保存完全相反而已
再看一下ViewGroup的分发:

    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
        super.dispatchRestoreInstanceState(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.dispatchRestoreInstanceState(container);
            }
        }
    }

依然是一样,遍历子View调用c.dispatchRestoreInstanceState,对于控件要重写这个方法来实现获取数据,还是以TextView为例:

    public void onRestoreInstanceState(Parcelable state) {
        if (!(state instanceof SavedState)) {
            super.onRestoreInstanceState(state);
            return;
        }

        SavedState ss = (SavedState)state;
        super.onRestoreInstanceState(ss.getSuperState());

        // XXX restore buffer type too, as well as lots of other stuff
        if (ss.text != null) {
            setText(ss.text);
        }

        if (ss.selStart >= 0 && ss.selEnd >= 0) {
            if (mText instanceof Spannable) {
                int len = mText.length();

                if (ss.selStart > len || ss.selEnd > len) {
                    String restored = "";

                    if (ss.text != null) {
                        restored = "(restored) ";
                    }

                    Log.e(LOG_TAG, "Saved cursor position " + ss.selStart +
                          "/" + ss.selEnd + " out of range for " + restored +
                          "text " + mText);
                } else {
                    Selection.setSelection((Spannable) mText, ss.selStart, ss.selEnd);

                    if (ss.frozenWithFocus) {
                        createEditorIfNeeded();
                        mEditor.mFrozenWithFocus = true;
                    }
                }
            }
        }

        if (ss.error != null) {
            final CharSequence error = ss.error;
            // Display the error later, after the first layout pass
            post(new Runnable() {
                public void run() {
                    if (mEditor == null || !mEditor.mErrorWasChanged) {
                        setError(error);
                    }
                }
            });
        }

        if (ss.editorState != null) {
            createEditorIfNeeded();
            mEditor.restoreInstanceState(ss.editorState);
        }
    }

可以看出量过程实际上完全相同。

从正常活着,到不正常死亡,再到死亡后重生,到此就算是讲完了Activity的生命了。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Qi T

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值