Android后臺殺死系列之一:FragmentActivity及PhoneWindow后臺殺死處理機(jī)制

新Activity啟動及舊Activity保存流程

App在后臺久置后,再次從桌面或最近的任務(wù)列表喚醒時經(jīng)常會發(fā)生崩潰,這往往是App在后臺被系統(tǒng)殺死,再次恢復(fù)的時候遇到了問題,而在使用FragmentActivity+Fragment的時候會更加頻繁。比如,如果Fragment沒有提供默認(rèn)構(gòu)造方法,就會在重建的時候因?yàn)榉瓷鋭?chuàng)建Fragment失敗而崩潰,再比如,在onCreate里面new 一個FragmentDialog,并且show,被后臺殺死后,再次喚醒的時候,就會show兩個對話框,這是為什么?其實(shí)這就涉及了后臺殺死及恢復(fù)的機(jī)制,其中涉及的知識點(diǎn)主要是FragmentActivity、ActivityManagerService、LowMemoryKiller機(jī)制、ActivityStack、Binder等一系列知識點(diǎn)。放在一篇文章里面可能會有些長,因此,Android后臺殺死系列寫了三篇:

本篇是Android后臺殺死系列的第一篇,主要講解在開發(fā)過程中,由于后臺殺死涉及的一些崩潰,以及如何避免這些崩潰,還有就是簡單的介紹一下onSaveInstanceState與onRestoreInstanceState執(zhí)行時機(jī)與原理,這兩個函數(shù)也是Android面試時常問的兩個點(diǎn),是比簡單的啟動模式Activity聲明周期稍微更深入細(xì)致一些的地方,也通過這個點(diǎn)引入后臺殺死及恢復(fù)原理。

FragmentActivity被后臺殺死后恢復(fù)邏輯

當(dāng)App被后臺異常殺死后,再次點(diǎn)擊icon,或者從最近任務(wù)列表進(jìn)入的時候,系統(tǒng)會幫助恢復(fù)當(dāng)時的場景,重新創(chuàng)建Activity,對于FragmentActivity,由于其中有Framgent,邏輯會相對再復(fù)雜一些,系統(tǒng)會首先重建被銷毀的Fragment。

舉個栗子

我們創(chuàng)建一個Activity,并且在onCreate函數(shù)中新建并show一個DialogFragment,之后通過某種方式將APP異常殺死(RogueKiller模擬后臺殺死工具),再次從最近的任務(wù)喚起App的時候,會發(fā)現(xiàn)顯示了兩個DialogFragment,代碼如下:

public class DialogFragmentActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DialogFragment dialogFragment = new FragmentDlg();
        dialogFragment.show(getSupportFragmentManager(), "");
    }

這不僅讓我們奇怪,為什么呢?雖然被殺死了,但是onCreate函數(shù)在執(zhí)行的時候還是只執(zhí)行了一次啊,為什么會出現(xiàn)兩個DialogFragment,這里其實(shí)就有一個DialogFragment是通過Android自身的恢復(fù)重建機(jī)制重建出來,在異常殺死的情況下onCreate(Bundle savedInstanceState)函數(shù)的savedInstanceState參數(shù)也不是null,而是包含了被殺死時所保存的場景信息。再來看個崩潰的例子,新建一個CrashFragment,并且丟棄默認(rèn)無參構(gòu)造方法:

public class CrashFragment extends Fragment {

    public CrashFragment(String tag) {
        super();
    }
}

之后再Activity中Add或replace添加這個CrashFragment,在CrashFragment顯示后,通過RogueKiller模擬后臺殺死工具模擬后臺殺死,再次從最近任務(wù)列表里喚起App的時候,就會遇到崩潰,

Caused by: android.support.v4.app.Fragment$InstantiationException: 
  Unable to instantiate fragment xxx.CrashFragment: 
  make sure class name exists, is public, and has an empty constructor that is public
        at android.support.v4.app.Fragment.instantiate(Fragment.java:431)
        at android.support.v4.app.FragmentState.instantiate(Fragment.java:102)
        at android.support.v4.app.FragmentManagerImpl.restoreAllState(FragmentManager.java:1952)
        at android.support.v4.app.FragmentController.restoreAllState(FragmentController.java:144)
        at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:307)
        at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:81)

上面的這兩個問題主要涉及后臺殺死后FragmentActivity自身的恢復(fù)機(jī)制,其實(shí)super.onCreate(savedInstanceState)在恢復(fù)時做了很多我們沒有看到的事情,先看一下崩潰:

為什么Fragment沒有無參構(gòu)造方法會引發(fā)崩潰

看一下support-V4中FragmentActivity中onCreate代碼如下:

protected void onCreate(@Nullable Bundle savedInstanceState) {
    mFragments.attachHost(null /*parent*/);

    super.onCreate(savedInstanceState);
                    ...
    if (savedInstanceState != null) {
        Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
        mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
    }
    mFragments.dispatchCreate();
}

可以看到如果savedInstanceState != null,就會執(zhí)行mFragments.restoreAllState邏輯,其實(shí)這里就牽扯到恢復(fù)時重建邏輯,再被后臺異常殺死前,或者說在Activity的onStop執(zhí)行前,Activity的現(xiàn)場以及Fragment的現(xiàn)場都是已經(jīng)被保存過的,其實(shí)是被保存早ActivityManagerService中,保存的格式FragmentState,重建的時候,會采用反射機(jī)制重新創(chuàng)Fragment

void restoreAllState(Parcelable state, List<Fragment> nonConfig) {
 
     ...
         for (int i=0; i<fms.mActive.length; i++) {
        FragmentState fs = fms.mActive[i];
        if (fs != null) {
            Fragment f = fs.instantiate(mHost, mParent);
            mActive.add(f);
    ...

其實(shí)就是調(diào)用FragmentState的instantiate,進(jìn)而調(diào)用Fragment的instantiate,最后通過反射,構(gòu)建Fragment,也就是,被加到FragmentActivity的Fragment在恢復(fù)的時候,會被自動創(chuàng)建,并且采用Fragment的默認(rèn)無參構(gòu)造方法,如果沒喲這個方法,就會拋出InstantiationException異常,這也是為什么第二個例子中會出現(xiàn)崩潰的原因。

 */
public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
    try {
        Class<?> clazz = sClassMap.get(fname);
        if (clazz == null) {
            // Class not found in the cache, see if it's real, and try to add it
            clazz = context.getClassLoader().loadClass(fname);
            sClassMap.put(fname, clazz);
        }
        Fragment f = (Fragment)clazz.newInstance();
        if (args != null) {
            args.setClassLoader(f.getClass().getClassLoader());
            f.mArguments = args;
        }
        return f;
    } catch (ClassNotFoundException e) {
        throw new InstantiationException("Unable to instantiate fragment " + fname
                + ": make sure class name exists, is public, and has an"
                + " empty constructor that is public", e);
    } catch (java.lang.InstantiationException e) {
        throw new InstantiationException("Unable to instantiate fragment " + fname
                + ": make sure class name exists, is public, and has an"
                + " empty constructor that is public", e);
    } catch (IllegalAccessException e) {
        throw new InstantiationException("Unable to instantiate fragment " + fname
                + ": make sure class name exists, is public, and has an"
                + " empty constructor that is public", e);
    }
}
q

可以看到場景二提示的errormsg跟拋出的異常是可以對應(yīng)上的,其實(shí)Fragment源碼里面也說得很清楚:

 /**
 * Default constructor.  <strong>Every</strong> fragment must have an
 * empty constructor, so it can be instantiated when restoring its
 * activity's state.  It is strongly recommended that subclasses do not
 * have other constructors with parameters, since these constructors
 * will not be called when the fragment is re-instantiated; instead,
 * arguments can be supplied by the caller with {@link #setArguments}
 * and later retrieved by the Fragment with {@link #getArguments}.
 * 
 * <p>Applications should generally not implement a constructor.  The
 * first place application code an run where the fragment is ready to
 * be used is in {@link #onAttach(Activity)}, the point where the fragment
 * is actually associated with its activity.  Some applications may also
 * want to implement {@link #onInflate} to retrieve attributes from a
 * layout resource, though should take care here because this happens for
 * the fragment is attached to its activity.
 */
 
public Fragment() {
}

大意就是,F(xiàn)ragment必須有一個空構(gòu)造方法,這樣才能保證重建流程,并且,F(xiàn)ragment的子類也不推薦有帶參數(shù)的構(gòu)造方法,最好采用setArguments來保存參數(shù)。下面再來看下為什么會出現(xiàn)兩個DialogFragment。

為什么出現(xiàn)兩個DialogFragment

Fragment在被創(chuàng)建之后,如果不通過add或者replace添加到Activity的布局中是不會顯示的,在保存現(xiàn)場的時候,也是保存了add的這個狀態(tài)的,來看一下Fragment的add邏輯:此時被后臺殺死,或旋轉(zhuǎn)屏幕,被恢復(fù)的DialogFragmentActivity時會出現(xiàn)兩個FragmentDialog,一個被系統(tǒng)恢復(fù)的,一個新建的。

<a name="add_fragment"/>

Add一個Fragment,并顯示的原理--所謂Fragment生命周期

通常我們FragmentActivity使用Fragment的方法如下:假設(shè)是在oncreate函數(shù)中:

@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Fragment fr = Fragment.instance("")
        getSupportFragmentManager().beginTransaction()
        .add(R.id.container,fr).commit();

其中g(shù)etSupportFragmentManager返回的是FragmentManager的子類FragmentManagerImpl,F(xiàn)ragmentManagerImpl是FragmentActivity的一個內(nèi)部類,其Fragment的管理邏輯都是由FragmentManagerImpl來處理的,本文是基于4.3,后面的高版本引入了FragmentController其實(shí)也只是多了一層封裝,原理差別不是太大,有興趣可以自己分析:

public class FragmentActivity extends Activity{
    ...
    final FragmentManagerImpl mFragments = new FragmentManagerImpl();
   ...
    final FragmentContainer mContainer = new FragmentContainer() {
        @Override
        @Nullable
        public View findViewById(int id) {
            return FragmentActivity.this.findViewById(id);
        }

        @Override
        public boolean hasView() {
            Window window = FragmentActivity.this.getWindow();
            return (window != null && window.peekDecorView() != null);
        }
    };

FragmentManagerImpl的beginTransaction()函數(shù)返回的是一個BackStackRecord()

@Override
public FragmentTransaction beginTransaction() {
    return new (this);
}

從名字就可以看出,beginTransaction是為FragmentActivity生成一條Transaction(事務(wù)),可以執(zhí)行,也可以反向,作為退棧的一個依據(jù),F(xiàn)ragmentTransaction的add函數(shù)實(shí)現(xiàn)如下,

public FragmentTransaction add(Fragment fragment, String tag) {
    doAddOp(0, fragment, tag, OP_ADD);//異步操作的,跟Hander類似
    return this;
}


private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
    fragment.mFragmentManager = mManager;
     ...
    Op op = new Op();
    op.cmd = opcmd;
    op.fragment = fragment;
    addOp(op);
}

之后commit這個Transaction, 將Transaction插入到Transaction隊(duì)列中去,最終會回調(diào)FragmentManager的addFragment方法,將Fragment添加FragmentManagerImpl到維護(hù)Fragment列表中去,并且根據(jù)當(dāng)前的Activity狀態(tài),將Fragment調(diào)整到合適的狀態(tài),代碼如下:

public void addFragment(Fragment fragment, boolean moveToStateNow) {

    if (mAdded == null) {
        mAdded = new ArrayList<Fragment>();
    }

    makeActive(fragment);
    
    if (!fragment.mDetached) {
        if (mAdded.contains(fragment)) {
            throw new IllegalStateException("Fragment already added: " + fragment);
        }
        mAdded.add(fragment);
        fragment.mAdded = true;
        fragment.mRemoving = false;
        if (fragment.mHasMenu && fragment.mMenuVisible) {
            mNeedMenuInvalidate = true;
        }
        if (moveToStateNow) {
            moveToState(fragment);
        }
    }
}    

為什么說FragmentManager是FragmentActivity管理Fragment的核心呢,請看下面:

final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory {
    ...
    
    ArrayList<Runnable> mPendingActions;
    Runnable[] mTmpActions;
    boolean mExecutingActions;
    
    ArrayList<Fragment> mActive;
    ArrayList<Fragment> mAdded;
    ArrayList<Integer> mAvailIndices;
    ArrayList<BackStackRecord> mBackStack;

可以看出FragmentManagerImpl幫FragmentActivity維護(hù)著所有管理Fragment的列表,F(xiàn)ragmentManagerImpl的State是和Activity的State一致的,這是管理Fragment的關(guān)鍵。其實(shí)Fragment自身是沒有什么生命周期的,它只是一個View的封裝,完全依靠FragmentManagerImpl來進(jìn)行同步模擬生命周期,比如在onCreate函數(shù)中創(chuàng)建Fragment,add后,在執(zhí)行的到Activity自身的onCreateView之前,F(xiàn)ragment的onCreateView是不會執(zhí)行的,也就是Fragment是被動式的跟FragmentActivity保持一致。既然Fragment只是個View的封裝,那么它是如何轉(zhuǎn)換成View,并添加到Container中去的呢?關(guān)鍵是moveToState函數(shù),這個函數(shù)強(qiáng)制將新add的Fragment的生命周期與Activity同步:

 void moveToState(Fragment f, int newState, int transit, int transitionStyle,
        boolean keepActive) {
        ...        
     if (f.mState < newState) { //低于當(dāng)前Activity的狀態(tài)
        switch (f.mState) {
            case Fragment.INITIALIZING:
                    ...
                f.mActivity = mActivity;
                f.mParentFragment = mParent;
                f.mFragmentManager = mParent != null
                        ? mParent.mChildFragmentManager : mActivity.mFragments;
                f.mCalled = false;
                f.onAttach(mActivity);
               ...
                if (!f.mRetaining) {
                    f.performCreate(f.mSavedFragmentState);
                } 
            case Fragment.CREATED:
                if (newState > Fragment.CREATED) {
            
                      f.mView = f.performCreateView(f.getLayoutInflater(
                      f.mSavedFragmentState), container, f.mSavedFragmentState);
                      f.onViewCreated(f.mView, f.mSavedFragmentState);
                 
                    f.performActivityCreated(f.mSavedFragmentState);
                    if (f.mView != null) {
                        f.restoreViewState(f.mSavedFragmentState);
                    }
                    f.mSavedFragmentState = null;
                }
            case Fragment.ACTIVITY_CREATED:
            case Fragment.STOPPED:
                    if (newState > Fragment.STOPPED) {
                        f.performStart();
                    }
            case Fragment.STARTED:
                if (newState > Fragment.STARTED) {
                   f.mResumed = true;
                    f.performResume();

可以看出,add Fragment之后,需要讓Fragment跟當(dāng)前Activity的State保持一致。現(xiàn)在回歸正題,對于后臺殺死狀態(tài)下,為什么會show兩個DialogFragment呢,我們需要接著看就要Fragment的異常處理的流程,在Fragment沒有無參構(gòu)造方法會引發(fā)崩潰里面,分析只是走到了Fragment的構(gòu)建,現(xiàn)在接著往下走。提供無參構(gòu)造函數(shù)后,F(xiàn)ragment可以正確的新建出來,之后呢?之后就是一些恢復(fù)邏輯,接著看restoreAllState

void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) {

    if (state == null) return;
    FragmentManagerState fms = (FragmentManagerState)state;
    mActive = new ArrayList<Fragment>(fms.mActive.length);
     for (int i=0; i<fms.mActive.length; i++) {
        FragmentState fs = fms.mActive[i];
        if (fs != null) {
            Fragment f = fs.instantiate(mActivity, mParent);

            mActive.add(f);
            fs.mInstance = null;

    // Build the list of currently added fragments.
    if (fms.mAdded != null) {
        mAdded = new ArrayList<Fragment>(fms.mAdded.length);
        for (int i=0; i<fms.mAdded.length; i++) {
            Fragment f = mActive.get(fms.mAdded[i]);
            if (f == null) {
                throwException(new IllegalStateException(
                        "No instantiated fragment for index #" + fms.mAdded[i]));
            }
            f.mAdded = true;
            if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f);
            if (mAdded.contains(f)) {
                throw new IllegalStateException("Already added!");
            }
            mAdded.add(f);
        }
    
    // Build the back stack.
    if (fms.mBackStack != null) {
        mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);
        for (int i=0; i<fms.mBackStack.length; i++) {
            BackStackRecord bse = fms.mBackStack[i].instantiate(this);

            mBackStack.add(bse);
            if (bse.mIndex >= 0) {
                setBackStackIndex(bse.mIndex, bse);
}

其實(shí)到現(xiàn)在現(xiàn)在Fragment相關(guān)的信息已經(jīng)恢復(fù)成功了,之后隨著FragmentActivity周期顯示或者更新了,這些都是被殺死后,在FragmentActiivyt的onCreate函數(shù)處理的,也就是默認(rèn)已經(jīng)將之前的Fragment添加到mAdded列表中去了,但是,在場景一,我們有手動新建了一個Fragment,并添加進(jìn)去,所以,mAdded函數(shù)中就有連個兩個Fragment。這樣,在FragmentActivity調(diào)用onStart函數(shù)之后,會新建mAdded列表中Fragment的視圖,將其添加到相應(yīng)的container中去,并在Activity調(diào)用onReusume的時候,顯示出來做的,這個時候,就會顯示兩份,其實(shí)如果,在這個時候,你再殺死一次,恢復(fù),就會顯示三分,在殺死,重啟,就是四份。。。。

@Override
protected void onStart() {
    super.onStart();

    mStopped = false;
    mReallyStopped = false;
    mHandler.removeMessages(MSG_REALLY_STOPPED);

    if (!mCreated) {
        mCreated = true;
        mFragments.dispatchActivityCreated();
    }

    mFragments.noteStateNotSaved();
    mFragments.execPendingActions();

    mFragments.doLoaderStart();

    // NOTE: HC onStart goes here.

    mFragments.dispatchStart();
    mFragments.reportLoaderStart();
}

以上就是針對兩個場景,對FramgentActivity的一些分析,主要是回復(fù)時候,對于Framgent的一些處理。

<a name="onSaveInstanceState_OnRestoreInstance"/>

onSaveInstanceState與OnRestoreInstance的調(diào)用時機(jī)

在在點(diǎn)擊home鍵,或者跳轉(zhuǎn)其他界面的時候,都會回調(diào)用onSaveInstanceState,但是再次喚醒卻不一定調(diào)用OnRestoreInstance,這是為什么呢?onSaveInstanceState與OnRestoreInstance難道不是配對使用的?在Android中,onSaveInstanceState是為了預(yù)防Activity被后臺殺死的情況做的預(yù)處理,如果Activity沒有被后臺殺死,那么自然也就不需要進(jìn)行現(xiàn)場的恢復(fù),也就不會調(diào)用OnRestoreInstance,而大多數(shù)情況下,Activity不會那么快被殺死。

onSaveInstanceState的調(diào)用時機(jī)

onSaveInstanceState函數(shù)是Android針對可能被后臺殺死的Activity做的一種預(yù)防,它的執(zhí)行時機(jī)在2.3之前是在onPause之前,2.3之后,放在了onStop函數(shù)之前,也就說Activity失去焦點(diǎn)后,可能會由于內(nèi)存不足,被回收的情況下,都會去執(zhí)行onSaveInstanceState。對于startActivity函數(shù)的調(diào)用很多文章都有介紹,可以簡單參考下老羅的博客Android應(yīng)用程序內(nèi)部啟動Activity過程(startActivity)的源代碼分析,比如在Activity A 調(diào)用startActivity啟動Activity B的時候,會首先通過AMS pause Activity A,之后喚起B(yǎng),在B顯示,再stop A,在stop A的時候,需要保存A的現(xiàn)場,因?yàn)椴豢梢姷腁ctivity都是可能被后臺殺死的,比如,在開發(fā)者選項(xiàng)中打開不保留活動,就會達(dá)到這種效果,在啟動另一個Activity時,上一個Activity的保存流程大概如下,這里先簡單描述,在下一篇原理篇的時候,會詳細(xì)講解下流程:

恢復(fù)啟動流程.png

在2.3之后,onSaveInstanceState的時機(jī)都放在了onStop之前,看一下FragmentActivity的onSaveInstanceState源碼:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Parcelable p = mFragments.saveAllState();
    if (p != null) {
        outState.putParcelable(FRAGMENTS_TAG, p);
    }
}

可以看出,首先就是父類的onSaveInstanceState,主要是保存一些窗口及View的信息,比如ViewPager當(dāng)前顯示的是第幾個View等。之后,就是就是通過FragmentManager的saveAllState,來保存FragmentActivity自身的現(xiàn)場-Fragment的一些狀態(tài),這些數(shù)據(jù)是FragmentActivity恢復(fù)Framgent所必須的數(shù)據(jù),處理不好就會出現(xiàn)上面的那種異常。

OnRestoreInstanceState的調(diào)用時機(jī)

之前已經(jīng)說過,OnRestoreInstanceState雖然與onSaveInstanceState是配對實(shí)現(xiàn)的,但是其調(diào)用卻并非完全成對的,在Activity跳轉(zhuǎn)或者返回主界面時,onSaveInstanceState是一定會調(diào)用的,但是OnRestoreInstanceState卻不會,它只有Activity或者App被異常殺死,走恢復(fù)流程的時候才會被調(diào)用。如果沒有被異常殺死,不走Activity的恢復(fù)新建流程,也就不會回調(diào)OnRestoreInstanceState,簡單看一下Activity的加載流程圖:

onRestoreInstance調(diào)用時機(jī).png

可以看出,OnRestoreInstanceState的調(diào)用時機(jī)是在onStart之后,在onPostCreate之前。那么正常的創(chuàng)建為什么沒調(diào)用呢?看一下ActivityThread中啟動Activity的源碼:

 private Activity performLaunchActivity(Activi
         
         ...
          mInstrumentation.callActivityOnCreate(activity, r.state);
               
               r.activity = activity;
               r.stopped = true;
               if (!r.activity.mFinished) {
                   activity.performStart();
                   r.stopped = false;
               }
               if (!r.activity.mFinished) {
                   if (r.state != null) {
                       mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                   }
               }
               if (!r.activity.mFinished) {
                   activity.mCalled = false;
                   mInstrumentation.callActivityOnPostCreate(activity, r.state);
            
               }
     }

可以看出,只有r.state != null的時候,才通過mInstrumentation.callActivityOnRestoreInstanceState回調(diào)OnRestoreInstanceState,r.state就是ActivityManagerService通過Binder傳給ActivityThread數(shù)據(jù),主要用來做場景恢復(fù)。以上就是onSaveInstanceState與OnRestoreInstance執(zhí)行時機(jī)的一些分析。下面結(jié)合具體的系統(tǒng)View控件來分析一下這兩個函數(shù)的具體應(yīng)用:比如ViewPager與FragmentTabHost,這兩個空間是主界面最常用的控件,內(nèi)部對后臺殺死做了兼容,這也是為什么被殺死后,Viewpager在恢復(fù)后,能自動定位到上次瀏覽的位置。

ViewPager應(yīng)對后臺殺死做的兼容

首先看一下ViewPager做的兼容,ViewPager在后臺殺死的情況下,仍然能恢復(fù)到上次關(guān)閉的位置,這也是對體驗(yàn)的一種優(yōu)化,這其中的原理是什么?之前分析onSaveInstanceState與onRestoreInstanceState的時候,只關(guān)注了Fragment的處理,其實(shí)還有一些針對Window窗口及Vie的處理,先看一下onSaveInstanceState針對窗口保存了什么:

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

PhonwWinow.java

@Override
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
      View focusedView = mContentParent.findFocus();
      ...
      outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
    // save the panels
    if (panelStates.size() > 0) {
        outState.putSparseParcelableArray(PANELS_TAG, panelStates);
    }
    if (mActionBar != null) {
        outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
    }

    return outState;
}

Window其實(shí)就是PhonwWinow,saveHierarchyState其實(shí)就是針對當(dāng)前窗口中的View保存一些場景信息 ,比如:當(dāng)前獲取焦點(diǎn)的View的id、ActionBar、View的一些狀態(tài),當(dāng)然saveHierarchyState遞歸遍歷所有子View,保存所有需要保存的狀態(tài):

ViewGroup.java

@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);
        }
    }
}

可見,該函數(shù)首先通過super.dispatchSaveInstanceState保存自身的狀態(tài),再遞歸傳遞給子View。onSaveInstanceState主要用于獲取View需要保存的State,并將自身的ID作為Key,存儲到SparseArray<Parcelable> states列表中,其實(shí)就PhoneWindow的一個列表,這些數(shù)據(jù)最后會通過Binder保存到ActivityManagerService中去

View.java

  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) {
            container.put(mID, state);
        }
    }
}

那么針對ViewPager到底存儲了什么信息?通過下面的代碼很容易看出,其實(shí)就是新建個了一個SavedState場景數(shù)據(jù),并且將當(dāng)前的位置mCurItem存進(jìn)去。

  @Override
public Parcelable onSaveInstanceState() {
    Parcelable superState = super.onSaveInstanceState();
    SavedState ss = new SavedState(superState);
    ss.position = mCurItem;
    if (mAdapter != null) {
        ss.adapterState = mAdapter.saveState();
    }
    return ss;
}

到這里存儲的事情基本就完成了。接下來看一下ViewPager的恢復(fù)以及onRestoreInstanceState到底做了什么,

protected void onRestoreInstanceState(Bundle savedInstanceState) {
    if (mWindow != null) {
        Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
        if (windowState != null) {
            mWindow.restoreHierarchyState(windowState);
        }
    }
}

從代碼可以看出,其實(shí)就是獲取當(dāng)時保存的窗口信息,之后通過mWindow.restoreHierarchyState做數(shù)據(jù)恢復(fù),

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

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

對于ViewPager會發(fā)生什么?從源碼很容易看出,其實(shí)就是取出SavedState,并獲取到異常殺死的時候的位置,以便后續(xù)的恢復(fù),

ViewPager.java

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

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

    if (mAdapter != null) {
        mAdapter.restoreState(ss.adapterState, ss.loader);
        setCurrentItemInternal(ss.position, false, true);
    } else {
        mRestoredCurItem = ss.position;
        mRestoredAdapterState = ss.adapterState;
        mRestoredClassLoader = ss.loader;
    }
} 

以上就解釋了ViewPager是如何通過onSaveInstanceState與onRestoreInstanceState保存、恢復(fù)現(xiàn)場的。如果是ViewPager+FragmentAdapter的使用方式,就同時涉及FragmentActivity的恢復(fù)、也牽扯到Viewpager的恢復(fù),其實(shí)FragmentAdapter也同樣針對后臺殺死做了一些兼容,防止重復(fù)新建Fragment,看一下FragmentAdapter的源碼:

FragmentPagerAdapter.java

@Override
public Object instantiateItem(ViewGroup container, int position) {
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }

    final long itemId = getItemId(position);

    // Do we already have this fragment?
    <!--是否已經(jīng)新建了Fragment??-->
    
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    
    1 如果Activity中存在相應(yīng)Tag的Fragment,就不要通過getItem新建
    
    if (fragment != null) {
        mCurTransaction.attach(fragment);
    } else {
    2 如果Activity中不存在相應(yīng)Tag的Fragment,就需要通過getItem新建
        fragment = getItem(position);
        mCurTransaction.add(container.getId(), fragment,
                makeFragmentName(container.getId(), itemId));
    }
    if (fragment != mCurrentPrimaryItem) {
        FragmentCompat.setMenuVisibility(fragment, false);
        FragmentCompat.setUserVisibleHint(fragment, false);
    }

    return fragment;
}

從1與2 可以看出,通過后臺恢復(fù),在FragmentActivity的onCreate函數(shù)中,會重建Fragment列表,那些被重建的Fragment不會再次通過getItem再次創(chuàng)建,再來看一下相似的控件FragmentTabHost,F(xiàn)ragmentTabHost也是主頁常用的控件,F(xiàn)ragmentTabHost也有相應(yīng)的后臺殺死處理機(jī)制,從名字就能看出,這個是專門針對Fragment才創(chuàng)建出來的控件。

后臺殺死時View的保存及恢復(fù)

FragmentTabHost應(yīng)對后臺殺死做的兼容

FragmentTabHost其實(shí)跟ViewPager很相似,在onSaveInstanceState執(zhí)行的時候保存當(dāng)前位置,并在onRestoreInstanceState恢復(fù)postion,并重新賦值給Tabhost,之后FragmentTabHost在onAttachedToWindow時,就可以根據(jù)恢復(fù)的postion設(shè)置當(dāng)前位置,代碼如下:

FragmentTabHost.java

@Override
protected Parcelable onSaveInstanceState() {
    Parcelable superState = super.onSaveInstanceState();
    SavedState ss = new SavedState(superState);
    ss.curTab = getCurrentTabTag();
    return ss;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
    if (!(state instanceof SavedState)) {
        super.onRestoreInstanceState(state);
        return;
    }
    SavedState ss = (SavedState) state;
    super.onRestoreInstanceState(ss.getSuperState());
    setCurrentTabByTag(ss.curTab);
}

在FragmentTabHost執(zhí)行onAttachedToWindow時候,會首先getCurrentTabTag ,如果是經(jīng)歷了后臺殺死,這里得到的值其實(shí)是恢復(fù)的SavedState里的值,之后通過doTabChanged切換到響應(yīng)的Tab,注意這里切換的時候,F(xiàn)ragment由于已經(jīng)重建了,是不會再次新建的。

@Override
protected void onAttachedToWindow() {
    super.onAttachedToWindow();

    String currentTab = getCurrentTabTag();
    ...
    
    ft = doTabChanged(currentTab, ft);
    
    if (ft != null) {
        ft.commit();
        mFragmentManager.executePendingTransactions();
    }
}

App開發(fā)時針對后臺殺死處理方式

  • 最簡單的方式,但是效果一般:取消系統(tǒng)恢復(fù)

比如:針對FragmentActivity ,不重建:

protected void onCreate(Bundle savedInstanceState) {
     if (savedInstanceState != null) {
     savedInstanceState.putParcelable(“android:support:fragments”, null);}
     super.onCreate(savedInstanceState);
}  

如果是系統(tǒng)的Actvity改成是“android:fragments",不過這里需要注意:對于ViewPager跟FragmentTabHost不需要額外處理,處理了可能反而有反作用。

針對Window,如果不想讓View使用恢復(fù)邏輯,在基類的FragmentActivity中覆蓋onRestoreInstanceState函數(shù)即可。

protected void onRestoreInstanceState(Bundle savedInstanceState) {
}

當(dāng)然以上的做法都是比較粗暴的做法,最好還是順著Android的設(shè)計,在需要保存現(xiàn)場的地方保存,在需要恢復(fù)的地方,去除相應(yīng)的數(shù)據(jù)進(jìn)行恢復(fù)。以上就是后臺殺死針對FragmentActivity、onSaveInstanceState、onRestoreInstanceState的一些分析,后面會有兩篇針對后臺殺死原理,以及ActivityManagerService如何處理殺死及恢復(fù)的文章。

Android后臺殺死系列之一:FragmentActivity及PhoneWindow后臺殺死處理機(jī)制
Android后臺殺死系列之二:ActivityManagerService與App現(xiàn)場恢復(fù)機(jī)制
Android后臺殺死系列之三:LowMemoryKiller原理(4.3-6.0)
Android后臺殺死系列之四:Binder訃告原理
Android后臺殺死系列之五:Android進(jìn)程保活-自“裁”或者耍流氓

作者:看書的小蝸牛
原文鏈接: Android后臺殺死系列之一:FragmentActivity及PhoneWindow后臺殺死處理機(jī)制

僅供參考,歡迎糾錯指正

參考文檔

Fragment Transactions & Activity State Loss
Lowmemorykiller筆記
Fragment實(shí)例化,F(xiàn)ragment生命周期源碼分析
android.app.Fragment$InstantiationException的原因分析
Android Framework架構(gòu)淺析之【近期任務(wù)】
Android Low Memory Killer介紹
Android開發(fā)之InstanceState詳解
Square:從今天開始拋棄Fragment吧!
對Android近期任務(wù)列表(Recent Applications)的簡單分析
Android——內(nèi)存管理-lowmemorykiller 機(jī)制
ActivityStackSupervisor分析
A Deeper Look of ViewPager and FragmentStatePagerAdaper
View的onSaveInstanceState和onRestoreInstanceState過程分析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內(nèi)容