Android 後臺殺死及恢復的機制

App在後臺久置後,再次從桌面或最近的任務列表喚醒時常常會發生崩潰,這每每是App在後臺被系統殺死,再次恢復的時候遇到了問題,而在使用FragmentActivity+Fragment的時候,常常會遇到:好比Fragment沒有提供默認構造方法,就會重建的時候由於反射建立Fragment失敗而崩潰,再好比,在onCreate裏面new 一個FragmentDialog,而且show,在被後臺殺死,再次喚醒的時候,就會show兩個對話框,這是爲何?其實這就涉及了後臺殺死及恢復的機制。java

FragmentActivity被後臺殺死後恢復邏輯

當App被後臺異常殺死後,再次點擊icon,或者從最近任務列表進入的時候,系統會幫助恢復當時的場景,從新建立Activity,對於FragmentActivity,因爲其中有Framgent,邏輯會相對再複雜一些,系統會首先重建被銷燬的Fragment。看FragmentActivity的onCreat代碼:node

咱們建立一個Activity,而且在onCreate函數中新建並show一個DialogFragment,以後經過某種方式將APP異常殺死(RogueKiller模擬後臺殺死工具),再次從最近的任務喚起App的時候,會發現顯示了兩個DialogFragment,代碼以下:android

public class DialogFragmentActivity extends AppCompatActivity {

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

雖然被殺死了,可是onCreate函數在執行的時候仍是隻執行了一次啊,爲何會出現兩個DialogFragment,這裏其實就有一個DialogFragment是經過Android自身的恢復重建機制重建出來,在異常殺死的狀況下onCreate(Bundle savedInstanceState)函數的savedInstanceState參數也不是null,而是包含了被殺死時所保存的場景信息。再來看個崩潰的例子,新建一個CrashFragment,而且丟棄默認無參構造方法:後端

public class CrashFragment extends Fragment {

    public CrashFragment(String tag) {
        super();
    }
}
複製代碼

以後再Activity中Add或replace添加這個CrashFragment,在CrashFragment顯示後,經過RogueKiller模擬後臺殺死工具模擬後臺殺死,再次從最近任務列表裏喚起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自身的恢復機制,其實super.onCreate(savedInstanceState)在恢復時作了不少咱們沒有看到的事情,先看一下崩潰:bash

爲何Fragment沒有無參構造方法會引起崩潰

看一下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,就會執行mFragments.restoreAllState邏輯,其實這裏就牽扯到恢復時重建邏輯,再被後臺異常殺死前,或者說在Activity的onStop執行前,Activity的現場以及Fragment的現場都是已經被保存過的,實際上是被保存早ActivityManagerService中,保存的格式FragmentState,重建的時候,會採用反射機制從新創Fragmentcookie

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);
    ...
複製代碼

其實就是調用FragmentState的instantiate,進而調用Fragment的instantiate,最後經過反射,構建Fragment,也就是,被加到FragmentActivity的Fragment在恢復的時候,會被自動建立,而且採用Fragment的默認無參構造方法,若是沒喲這個方法,就會拋出InstantiationException異常,這也是爲何第二個例子中會出現崩潰的緣由。網絡

*/
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跟拋出的異常是能夠對應上的,其實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() {
}
複製代碼

大意就是,Fragment必須有一個空構造方法,這樣才能保證重建流程,而且,Fragment的子類也不推薦有帶參數的構造方法,最好採用setArguments來保存參數。下面再來看下爲何會出現兩個DialogFragment。架構

爲何出現兩個DialogFragment

Fragment在被建立以後,若是不經過add或者replace添加到Activity的佈局中是不會顯示的,在保存現場的時候,也是保存了add的這個狀態的,來看一下Fragment的add邏輯:此時被後臺殺死,或旋轉屏幕,被恢復的DialogFragmentActivity時會出現兩個FragmentDialog,一個被系統恢復的,一個新建的。

Add一個Fragment,並顯示的原理–所謂Fragment生命週期

一般咱們FragmentActivity使用Fragment的方法以下:假設是在oncreate函數中:

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

其中getSupportFragmentManager返回的是FragmentManager的子類FragmentManagerImpl,FragmentManagerImpl是FragmentActivity的一個內部類,其Fragment的管理邏輯都是由FragmentManagerImpl來處理的,本文是基於4.3,後面的高版本引入了FragmentController其實也只是多了一層封裝,原理差異不是太大,有興趣能夠本身分析:

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()函數返回的是一個BackStackRecord()

@Override
public FragmentTransaction beginTransaction() {
    return new (this);
}
複製代碼

從名字就能夠看出,beginTransaction是爲FragmentActivity生成一條Transaction(事務),能夠執行,也能夠反向,做爲退棧的一個依據,FragmentTransaction的add函數實現以下,

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隊列中去,最終會回調FragmentManager的addFragment方法,將Fragment添加FragmentManagerImpl到維護Fragment列表中去,而且根據當前的Activity狀態,將Fragment調整到合適的狀態,代碼以下:

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維護着全部管理Fragment的列表,FragmentManagerImpl的State是和Activity的State一致的,這是管理Fragment的關鍵。其實Fragment自身是沒有什麼生命週期的,它只是一個View的封裝,徹底依靠FragmentManagerImpl來進行同步模擬生命週期,好比在onCreate函數中建立Fragment,add後,在執行的到Activity自身的onCreateView以前,Fragment的onCreateView是不會執行的,也就是Fragment是被動式的跟FragmentActivity保持一致。既然Fragment只是個View的封裝,那麼它是如何轉換成View,並添加到Container中去的呢?關鍵是moveToState函數,這個函數強制將新add的Fragment的生命週期與Activity同步:

void moveToState(Fragment f, int newState, int transit, int transitionStyle,
        boolean keepActive) {
        ...        
     if (f.mState < newState) { //低於當前Activity的狀態
        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跟當前Activity的State保持一致。如今迴歸正題,對於後臺殺死狀態下,爲何會show兩個DialogFragment呢,咱們須要接着看就要Fragment的異常處理的流程,在Fragment沒有無參構造方法會引起崩潰裏面,分析只是走到了Fragment的構建,如今接着往下走。提供無參構造函數後,Fragment能夠正確的新建出來,以後呢?以後就是一些恢復邏輯,接着看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);
}
複製代碼

其實到如今如今Fragment相關的信息已經恢復成功了,以後隨着FragmentActivity週期顯示或者更新了,這些都是被殺死後,在FragmentActiivyt的onCreate函數處理的,也就是默認已經將以前的Fragment添加到mAdded列表中去了,可是,在場景一,咱們有手動新建了一個Fragment,並添加進去,因此,mAdded函數中就有連個兩個Fragment。這樣,在FragmentActivity調用onStart函數以後,會新建mAdded列表中Fragment的視圖,將其添加到相應的container中去,並在Activity調用onReusume的時候,顯示出來作的,這個時候,就會顯示兩份,其實若是,在這個時候,你再殺死一次,恢復,就會顯示三分,在殺死,重啓,就是四份。。。。

@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的一些分析,主要是回覆時候,對於Framgent的一些處理。

onSaveInstanceState與OnRestoreInstance的調用時機

在在點擊home鍵,或者跳轉其餘界面的時候,都會回調用onSaveInstanceState,可是再次喚醒卻不必定調用OnRestoreInstance,這是爲何呢?onSaveInstanceState與OnRestoreInstance難道不是配對使用的?在Android中,onSaveInstanceState是爲了預防Activity被後臺殺死的狀況作的預處理,若是Activity沒有被後臺殺死,那麼天然也就不須要進行現場的恢復,也就不會調用OnRestoreInstance,而大多數狀況下,Activity不會那麼快被殺死。

onSaveInstanceState的調用時機

onSaveInstanceState函數是Android針對可能被後臺殺死的Activity作的一種預防,它的執行時機在2.3以前是在onPause以前,2.3以後,放在了onStop函數以前,也就說Activity失去焦點後,可能會因爲內存不足,被回收的狀況下,都會去執行onSaveInstanceState。

好比在Activity A 調用startActivity啓動Activity B的時候,會首先經過AMS pause Activity A,以後喚起B,在B顯示,再stop A,在stop A的時候,須要保存A的現場,由於不可見的Activity都是可能被後臺殺死的,好比,在開發者選項中打開不保留活動,就會達到這種效果,在啓動另外一個Activity時,上一個Activity的保存流程大概以下

在2.3以後,onSaveInstanceState的時機都放在了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當前顯示的是第幾個View等。以後,就是就是經過FragmentManager的saveAllState,來保存FragmentActivity自身的現場-Fragment的一些狀態,這些數據是FragmentActivity恢復Framgent所必須的數據,處理很差就會出現上面的那種異常。

OnRestoreInstanceState的調用時機

OnRestoreInstanceState雖然與onSaveInstanceState是配對實現的,可是其調用卻並不是徹底成對的,在Activity跳轉或者返回主界面時,onSaveInstanceState是必定會調用的,可是OnRestoreInstanceState卻不會,它只有Activity或者App被異常殺死,走恢復流程的時候纔會被調用。若是沒有被異常殺死,不走Activity的恢復新建流程,也就不會回調OnRestoreInstanceState,簡單看一下Activity的加載流程圖:

能夠看出,OnRestoreInstanceState的調用時機是在onStart以後,在onPostCreate以前。那麼正常的建立爲何沒調用呢?看一下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回調OnRestoreInstanceState,r.state就是ActivityManagerService經過Binder傳給ActivityThread數據,主要用來作場景恢復。以上就是onSaveInstanceState與OnRestoreInstance執行時機的一些分析。下面結合具體的系統View控件來分析一下這兩個函數的具體應用:好比ViewPager與FragmentTabHost,這兩個空間是主界面最經常使用的控件,內部對後臺殺死作了兼容,這也是爲何被殺死後,Viewpager在恢復後,能自動定位到上次瀏覽的位置。

ViewPager應對後臺殺死作的兼容

首先看一下ViewPager作的兼容,ViewPager在後臺殺死的狀況下,仍然能恢復到上次關閉的位置,這也是對體驗的一種優化,這其中的原理是什麼?以前分析onSaveInstanceState與onRestoreInstanceState的時候,只關注了Fragment的處理,其實還有一些針對Window窗口及View的處理,先看一下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其實就是PhonwWinow,saveHierarchyState其實就是針對當前窗口中的View保存一些場景信息 ,好比:當前獲取焦點的View的id、ActionBar、View的一些狀態,固然saveHierarchyState遞歸遍歷全部子View,保存全部須要保存的狀態:

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);
        }
    }
}
複製代碼

可見,該函數首先經過super.dispatchSaveInstanceState保存自身的狀態,再遞歸傳遞給子View。onSaveInstanceState主要用於獲取View須要保存的State,並將自身的ID做爲Key,存儲到SparseArray states列表中,其實就PhoneWindow的一個列表,這些數據最後會經過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到底存儲了什麼信息?經過下面的代碼很容易看出,其實就是新建個了一個SavedState場景數據,而且將當前的位置mCurItem存進去。

@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的恢復以及onRestoreInstanceState到底作了什麼,

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

從代碼能夠看出,其實就是獲取當時保存的窗口信息,以後經過mWindow.restoreHierarchyState作數據恢復,

@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會發生什麼?從源碼很容易看出,其實就是取出SavedState,並獲取到異常殺死的時候的位置,以便後續的恢復,

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保存、恢復現場的。若是是ViewPager+FragmentAdapter的使用方式,就同時涉及FragmentActivity的恢復、也牽扯到Viewpager的恢復,其實FragmentAdapter也一樣針對後臺殺死作了一些兼容,防止重複新建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?
    <!--是否已經新建了Fragment??-->
    
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    
    1 若是Activity中存在相應Tag的Fragment,就不要經過getItem新建
    
    if (fragment != null) {
        mCurTransaction.attach(fragment);
    } else {
    2 若是Activity中不存在相應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 能夠看出,經過後臺恢復,在FragmentActivity的onCreate函數中,會重建Fragment列表,那些被重建的Fragment不會再次經過getItem再次建立,再來看一下類似的控件FragmentTabHost,FragmentTabHost也是主頁經常使用的控件,FragmentTabHost也有相應的後臺殺死處理機制,從名字就能看出,這個是專門針對Fragment才建立出來的控件。

FragmentTabHost應對後臺殺死作的兼容

FragmentTabHost其實跟ViewPager很類似,在onSaveInstanceState執行的時候保存當前位置,並在onRestoreInstanceState恢復postion,並從新賦值給Tabhost,以後FragmentTabHost在onAttachedToWindow時,就能夠根據恢復的postion設置當前位置,代碼以下:

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執行onAttachedToWindow時候,會首先getCurrentTabTag ,若是是經歷了後臺殺死,這裏獲得的值實際上是恢復的SavedState裏的值,以後經過doTabChanged切換到響應的Tab,注意這裏切換的時候,Fragment因爲已經重建了,是不會再次新建的。

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

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

App開發時針對後臺殺死處理方式

最簡單的方式,可是效果通常:取消系統恢復

好比:針對FragmentActivity ,不重建:

protected void onCreate(Bundle savedInstanceState) {
     if (savedInstanceState != null) {
        savedInstanceState.putParcelable(「android:support:fragments」, null);
     }
     super.onCreate(savedInstanceState);
}  
複製代碼

若是是系統的Actvity改爲是「android:fragments」,不過這裏須要注意:對於ViewPager跟FragmentTabHost不須要額外處理,處理了可能反而有副作用。

針對Window,若是不想讓View使用恢復邏輯,在基類的FragmentActivity中覆蓋onRestoreInstanceState函數便可。

protected void onRestoreInstanceState(Bundle savedInstanceState) {
}
複製代碼

固然以上的作法都是比較粗暴的作法,最好仍是順着Android的設計,在須要保存現場的地方保存,在須要恢復的地方,去除相應的數據進行恢復。

ActivityManagerService恢復App現場機制

假設,一個應用被後臺殺死,再次從最近的任務列表喚起App時候,系統是如何處理的呢?

Android框架層(AMS)如何知道App被殺死了

首先來看第一個問題,系統如何知道Application被殺死了,Android使用了Linux的oomKiller機制,只是簡單的作了個變種,採用分等級的LowmemoryKiller,但這個實際上是內核層面,LowmemoryKiller殺死進程後,如何向用戶空間發送通知,並告訴框架層的ActivityMangerService呢?只有AMS在知道App或者Activity是否被殺死後,AMS(ActivityMangerService)才能正確的走重建或者喚起流程,好比,APP死了,可是因爲存在須要復活的Service,那麼這個時候,進程須要從新啓動,這個時候怎麼處理的,那麼AMS到底是在何時知道App或者Activity被後臺殺死了呢?咱們先看一下從最近的任務列表進行喚起的時候,究竟發生了什麼。

從最近的任務列表或者Icon再次喚起App流程

在系統源碼systemUi的包裏,有個RecentActivity,這個其實就是最近的任務列表的入口,而其呈現界面是經過RecentsPanelView來展示的,點擊最近的App其執行代碼以下:

public void handleOnClick(View view) {
    ViewHolder holder = (ViewHolder)view.getTag();
    TaskDescription ad = holder.taskDescription;
    final Context context = view.getContext();
    final ActivityManager am = (ActivityManager)
            context.getSystemService(Context.ACTIVITY_SERVICE);
    Bitmap bm = holder.thumbnailViewImageBitmap;
    ...
    // 關鍵點 1  若是TaskDescription沒有被主動關閉,正常關閉,ad.taskId就是>=0
    if (ad.taskId >= 0) {
        // This is an active task; it should just go to the foreground.
        am.moveTaskToFront(ad.taskId, ActivityManager.MOVE_TASK_WITH_HOME,
                opts);
    } else {
        Intent intent = ad.intent;
        intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
                | Intent.FLAG_ACTIVITY_TASK_ON_HOME
                | Intent.FLAG_ACTIVITY_NEW_TASK);
        try {
            context.startActivityAsUser(intent, opts,
                    new UserHandle(UserHandle.USER_CURRENT));
        }...
}
複製代碼

在上面的代碼裏面,有個判斷ad.taskId >= 0,若是知足這個條件,就經過moveTaskToFront喚起APP,那麼ad.taskId是如何獲取的?recent包裏面有各種RecentTasksLoader,這個類就是用來加載最近任務列表的一個Loader,看一下它的源碼,主要看一下加載:

@Override
protected Void doInBackground(Void... params) {
    // We load in two stages: first, we update progress with just the first screenful
    // of items. Then, we update with the rest of the items
    final int origPri = Process.getThreadPriority(Process.myTid());
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    final PackageManager pm = mContext.getPackageManager();
    final ActivityManager am = (ActivityManager)
    mContext.getSystemService(Context.ACTIVITY_SERVICE);
    
    final List<ActivityManager.RecentTaskInfo> recentTasks =
            am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
     
    ....
        TaskDescription item = createTaskDescription(recentInfo.id,
                recentInfo.persistentId, recentInfo.baseIntent,
                recentInfo.origActivity, recentInfo.description);
    ....
} 
複製代碼

能夠看到,其實就是經過ActivityManger的getRecentTasks向AMS請求最近的任務信息,而後經過createTaskDescription建立TaskDescription,這裏傳遞的recentInfo.id其實就是TaskDescription的taskId,來看一下它的意義:

public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
        int flags, int userId) {
        ...           
        IPackageManager pm = AppGlobals.getPackageManager();

        final int N = mRecentTasks.size();
        ...
        for (int i=0; i<N && maxNum > 0; i++) {
            TaskRecord tr = mRecentTasks.get(i);
            if (i == 0
                    || ((flags&ActivityManager.RECENT_WITH_EXCLUDED) != 0)
                    || (tr.intent == null)
                    || ((tr.intent.getFlags()
                            &Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0)) {
                ActivityManager.RecentTaskInfo rti
                        = new ActivityManager.RecentTaskInfo();
                rti.id = tr.numActivities > 0 ? tr.taskId : -1;
                rti.persistentId = tr.taskId;
                rti.baseIntent = new Intent(
                        tr.intent != null ? tr.intent : tr.affinityIntent);
                if (!detailed) {
                    rti.baseIntent.replaceExtras((Bundle)null);
                }
複製代碼

能夠看出RecentTaskInfo的id是由TaskRecord決定的,若是TaskRecord中numActivities > 0就去TaskRecord的Id,不然就取-1,這裏的numActivities其實就是TaskRecode中記錄的ActivityRecord的數目,更具體的細節能夠自行查看ActivityManagerService及ActivityStack,那麼這裏就容易解釋了,只要是存活的APP,或者被LowmemoryKiller殺死的APP,其AMS的ActivityRecord是完整保存的,這也是恢復的依據。對於RecentActivity獲取的數據其實就是AMS中的翻版,它也是不知道將要喚起的APP是不是存活的,只要TaskRecord告訴RecentActivity是存貨的,那麼久直接走喚起流程。也就是經過ActivityManager的moveTaskToFront喚起App,至於後續的工做,就徹底交給AMS來處理。現看一下到這裏的流程圖:

整個APP被後臺殺死的狀況下AMS是如何恢復現場的

AMS與客戶端的通訊是經過Binder來進行的,而且通訊是」全雙工「的,且互爲客戶端跟服務器,也就說AMS向客戶端發命令的時候,AMS是客戶端,反之亦然。注意 Binder有個訃告的功能的:若是基於Binder通訊的服務端(S)若是掛掉了,客戶端(C)可以收到Binder驅動發送的一份訃告,告知客戶端Binder服務掛了,能夠把Binder驅動看做是第三方不死郵政機構,專門向客戶端發偶像死亡通知。對於APP被異常殺死的狀況下,這份訃告是發送給AMS的,AMS在收到通知後,就會針對APP被異常殺死的狀況做出整理,這裏牽扯到Binder驅動的代碼有興趣能夠本身翻一下。之類直接衝訃告接受後端處理邏輯來分析,在AMS源碼中,入口其實就是appDiedLocked.

final void appDiedLocked(ProcessRecord app, int pid,
            IApplicationThread thread) {
			  ...
	        if (app.pid == pid && app.thread != null &&
                app.thread.asBinder() == thread.asBinder()) {
            boolean doLowMem = app.instrumentationClass == null;
            關鍵點1 
            handleAppDiedLocked(app, false, true);
        	 // 若是是被後臺殺了,怎麼處理
        	 關鍵點2 
            if (doLowMem) {
                boolean haveBg = false;
                for (int i=mLruProcesses.size()-1; i>=0; i--) {
                    ProcessRecord rec = mLruProcesses.get(i);
                    if (rec.thread != null && rec.setAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
                        haveBg = true;
                        break;
                    }
                }
                if (!haveBg) {
                <!--若是被LowmemoryKiller殺了,就說明內存緊張,這個時候就會通知其餘後臺APP,當心了,趕忙釋放資源-->
                    EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mLruProcesses.size());
                    long now = SystemClock.uptimeMillis();
                    for (int i=mLruProcesses.size()-1; i>=0; i--) {
                        ProcessRecord rec = mLruProcesses.get(i);
                        if (rec != app && rec.thread != null &&
                                (rec.lastLowMemory+GC_MIN_INTERVAL) <= now) {
                            if (rec.setAdj <= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
                                rec.lastRequestedGc = 0;
                            } else {
                                rec.lastRequestedGc = rec.lastLowMemory;
                            }
                            rec.reportLowMemory = true;
                            rec.lastLowMemory = now;
                            mProcessesToGc.remove(rec);
                            addProcessToGcListLocked(rec);
                        }
                    }
                    mHandler.sendEmptyMessage(REPORT_MEM_USAGE);
                    <!--縮減資源-->
                    scheduleAppGcsLocked();
                }
            }
        }
        ...
    }
複製代碼

先看關鍵點1:在進程被殺死後,AMS端要選擇性清理進程相關信息,清理後,再根據是否是內存低引發的後臺殺死,決定是否是須要清理其餘後臺進程。接着看handleAppDiedLocked如何清理的,這裏有重建時的依據:ActivityRecord不清理,可是爲它設置個APP未綁定的標識

private final void handleAppDiedLocked(ProcessRecord app,
        boolean restarting, boolean allowRestart) {
    
    關鍵點1
    cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);
    ...
    關鍵點2
     // Remove this application's activities from active lists. boolean hasVisibleActivities = mMainStack.removeHistoryRecordsForAppLocked(app); app.activities.clear(); ... 關鍵點3 if (!restarting) { if (!mMainStack.resumeTopActivityLocked(null)) { // If there was nothing to resume, and we are not already // restarting this process, but there is a visible activity that // is hosted by the process... then make sure all visible // activities are running, taking care of restarting this // process. if (hasVisibleActivities) { mMainStack.ensureActivitiesVisibleLocked(null, 0); } } } } 複製代碼

看關鍵點1,cleanUpApplicationRecordLocked,主要負責清理一些Providers,receivers,service之類的信息,而且在清理過程當中根據配置的一些信息決定是否須要重建進程並啓動,關鍵點2 就是關係到喚起流程的判斷,關鍵點3,主要是被殺的進程是不是當前前臺進程,若是是,須要重建,並當即顯示:先簡單看cleanUpApplicationRecordLocked的清理流程

private final void cleanUpApplicationRecordLocked(ProcessRecord app,
        boolean restarting, boolean allowRestart, int index) {
        
   <!--清理service-->
    mServices.killServicesLocked(app, allowRestart);
    ...
    boolean restart = false;
   <!--清理Providers.-->
    if (!app.pubProviders.isEmpty()) {
        Iterator<ContentProviderRecord> it = app.pubProviders.values().iterator();
        while (it.hasNext()) {
            ContentProviderRecord cpr = it.next();
			。。。
        app.pubProviders.clear();
    } ...
     <!--清理receivers.-->
     // Unregister any receivers.
    if (app.receivers.size() > 0) {
        Iterator<ReceiverList> it = app.receivers.iterator();
        while (it.hasNext()) {
            removeReceiverLocked(it.next());
        }
        app.receivers.clear();
    }
    ... 關鍵點1,進程是夠須要重啓,
    if (restart && !app.isolated) {
        // We have components that still need to be running in the
        // process, so re-launch it.
        mProcessNames.put(app.processName, app.uid, app);
        startProcessLocked(app, "restart", app.processName);
    } 
	 ...
}
複製代碼

從關鍵點1就能知道,這裏是隱藏了進程是否須要重啓的邏輯,好比一個Service設置了START_STICKY,被殺後,就須要從新喚起,這裏也是流氓軟件肆虐的緣由。再接着看mMainStack.removeHistoryRecordsForAppLocked(app),對於直觀理解APP重建 ,這句代碼處於核心的地位,

boolean removeHistoryRecordsForAppLocked(ProcessRecord app) {
    ...
    while (i > 0) {
        i--;
        ActivityRecord r = (ActivityRecord)mHistory.get(i);
        if (r.app == app) {
            boolean remove;
            <!--關鍵點1-->
            if ((!r.haveState && !r.stateNotNeeded) || r.finishing) {
                remove = true;
            } else if (r.launchCount > 2 &&
                remove = true;
            } else {
             //通常來說,走false
                remove = false;
            }
            <!--關鍵點2-->
            if (remove) {
                ...
                removeActivityFromHistoryLocked(r);

            } else {
                ...
                r.app = null;
                ...
    }

    return hasVisibleActivities;
}
複製代碼

在Activity跳轉的時候,準確的說,在stopActivity以前,會保存Activity的現場,這樣在AMS端r.haveState==true,也就是說,其ActivityRecord不會被從ActivityStack中移除,同時ActivityRecord的app字段被置空,這裏在恢復的時候,是決定resume仍是重建的關鍵。接着往下看moveTaskToFrontLocked,這個函數在ActivityStack中,主要管理ActivityRecord棧的,全部start的Activity都在ActivityStack中保留一個ActivityRecord,這個也是AMS管理Activiyt的一個依據,最終moveTaskToFrontLocked會調用resumeTopActivityLocked來喚起Activity,AMS獲取即將resume的Activity信息的方式主要是經過ActivityRecord,它並不知道Activity自己是否存活,獲取以後,AMS在喚醒Activity的環節才知道App或者Activity被殺死,具體看一下resumeTopActivityLocked源碼:

final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
		  ...
		 關鍵點1 
        if (next.app != null && next.app.thread != null) { 
        ...
        
        } else {
            // Whoops, need to restart this activity!
		  ...
            startSpecificActivityLocked(next, true, true);
        }

        return true;
    }
複製代碼

看關鍵點1的判斷條件,因爲已經將ActivityRecord的app字段置空,AMS就知道了這個APP或者Activity被異常殺死過,所以,就會走startSpecificActivityLocked進行重建。 其實仔細想一想很簡單,對於主動調用finish的,AMS並不會清理掉ActivitRecord,在喚起APP的時候,若是AMS檢測到APP還存活,就走scheduleResumeActivity進行喚起上一個Activity,可是若是APP或者Activity被異常殺死過,那麼AMS就經過startSpecificActivityLocked再次將APP重建,而且將最後的Activity重建。

APP存活,可是Activity被後臺殺死的狀況下AMS是如何恢復現場的

還有一種可能,APP沒有被kill,可是Activity被Kill掉了,這個時候會怎麼樣。首先,Activity的管理是必定經過AMS的,Activity的kill必定是是AMS操刀的,是有記錄的,嚴格來講,這種狀況並不屬於後臺殺死,由於這屬於AMS正常的管理,在可控範圍,好比打開了開發者模式中的「不保留活動」,這個時候,雖然會殺死Activity,可是仍然保留了ActivitRecord,因此再喚醒,或者回退的的時候仍然有跡可循,看一下ActivityStack的Destroy回調代碼,

final boolean destroyActivityLocked(ActivityRecord r,
            boolean removeFromApp, boolean oomAdj, String reason) {
        ...
        if (hadApp) {
          ...
           boolean skipDestroy = false;
            try {
             關鍵代碼 1
                r.app.thread.scheduleDestroyActivity(r.appToken, r.finishing,
                        r.configChangeFlags);
         	...
            if (r.finishing && !skipDestroy) {
                if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYING: " + r
                        + " (destroy requested)");
                r.state = ActivityState.DESTROYING;
                Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG);
                msg.obj = r;
                mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
            } else {
          關鍵代碼 2
                r.state = ActivityState.DESTROYED;
                if (DEBUG_APP) Slog.v(TAG, "Clearing app during destroy for activity " + r);
                r.app = null;
            }
        } 
        return removedFromHistory;
    }  
複製代碼

這裏有兩個關鍵啊你單,1是告訴客戶端的AcvitityThread清除Activity,2是標記若是AMS本身非正常關閉的Activity,就將ActivityRecord的state設置爲ActivityState.DESTROYED,而且清空它的ProcessRecord引用:r.app = null。這裏是喚醒時候的一個重要標誌,經過這裏AMS就能知道Activity被本身異常關閉了,設置ActivityState.DESTROYED是爲了讓避免後面的清空邏輯。

final void activityDestroyed(IBinder token) {
    synchronized (mService) {
        final long origId = Binder.clearCallingIdentity();
        try {
            ActivityRecord r = ActivityRecord.forToken(token);
            if (r != null) {
                mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
            }
           int index = indexOfActivityLocked(r);
            if (index >= 0) {
            1  <!--這裏會是否從history列表移除ActivityRecord-->
                if (r.state == ActivityState.DESTROYING) {
                    cleanUpActivityLocked(r, true, false);
                    removeActivityFromHistoryLocked(r);
                }
            }
            resumeTopActivityLocked(null);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }
}
複製代碼

看代碼關鍵點1,只有r.state == ActivityState.DESTROYING的時候,纔會移除ActivityRecord,可是對於不非正常finish的Activity,其狀態是不會被設置成ActivityState.DESTROYING,是直接跳過了ActivityState.DESTROYING,被設置成了ActivityState.DESTROYED,因此不會removeActivityFromHistoryLocked,也就是保留了ActivityRecord現場,好像也是依靠異常來區分是不是正常的結束掉Activity。這種狀況下是如何啓動Activity的呢? 經過上面兩點分析,就知道了兩個關鍵點

  • ActivityRecord沒有動HistoryRecord列表中移除
  • ActivityRecord 的ProcessRecord字段被置空,r.app = null

這樣就保證了在resumeTopActivityLocked的時候,走startSpecificActivityLocked分支

final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
		  ...
		 	
        if (next.app != null && next.app.thread != null) { 
        ...
        
        } else {
            // Whoops, need to restart this activity!
		  ...
            startSpecificActivityLocked(next, true, true);
        }

        return true;
    }
複製代碼

到這裏,AMS就知道了這個APP或者Activity是否是被異常殺死過,從而,決定是走resume流程仍是restore流程。

App被殺前的場景是如何保存:新Activity啓動跟舊Activity的保存

App現場的保存流程相對是比較簡單的,入口基本就是startActivity的時候,只要是界面的跳轉基本都牽扯到Activity的切換跟當前Activity場景的保存:先畫個簡單的圖形,開偏裏面講FragmentActivity的時候,簡單說了一些onSaveInstance的執行時機,這裏詳細看一下AMS是如何管理這些跳轉以及場景保存的,模擬場景:Activity A 啓動Activity B的時候,這個時候A不可見,可能會被銷燬,須要保存A的現場,這個流程是什麼樣的:簡述以下

  • ActivityA startActivity ActivityB
  • ActivityA pause
  • ActivityB create
  • ActivityB start
  • ActivityB resume
  • ActivityA onSaveInstance
  • ActivityA stop

流程大概是以下樣子:

如今咱們經過源碼一步一步跟一下,看看AMS在新Activity啓動跟舊Activity的保存的時候,到底作了什麼:跳過簡單的startActivity,直接去AMS中去看

ActivityManagerService

public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
        Intent intent, String resolvedType, IBinder resultTo,
        String resultWho, int requestCode, int startFlags,
        String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) {
    enforceNotIsolatedCaller("startActivity");
     ...
    return mMainStack.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
            resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
            null, null, options, userId);
}
複製代碼

ActivityStack

final int startActivityMayWait(IApplicationThread caller, int callingUid,
                  
        int res = startActivityLocked(caller, intent, resolvedType,
                aInfo, resultTo, resultWho, requestCode, callingPid, callingUid,
                callingPackage, startFlags, options, componentSpecified, null);
        
     。。。
} 
複製代碼

這裏經過startActivityMayWait啓動新的APP,或者新Activity,這裏只看簡單的,至於從桌面啓動App的流程,能夠去參考更詳細的文章,好比老羅的startActivity流程,大概就是新建ActivityRecord,ProcessRecord之類,並加入AMS中相應的堆棧等,resumeTopActivityLocked是界面切換的統一入口,第一次進來的時候,因爲ActivityA還在沒有pause,所以須要先暫停ActivityA,這些完成後,

ActivityStack

final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {       
     ...
     <!--必須將當前Resume的Activity設置爲pause 而後stop才能繼續-->
   // We need to start pausing the current activity so the top one
    // can be resumed...
    if (mResumedActivity != null) {            
        if (next.app != null && next.app.thread != null) {
            
            mService.updateLruProcessLocked(next.app, false);
        }
        startPausingLocked(userLeaving, false);
        return true;
        }
        ....
}
複製代碼

其實這裏就是暫停ActivityA,AMS經過Binder告訴ActivityThread須要暫停的ActivityA,ActivityThread完成後再經過Binder通知AMS,AMS會開始resume ActivityB,

private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) {

    if (prev.app != null && prev.app.thread != null) {
       ...
        try {
            prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
                    userLeaving, prev.configChangeFlags);
複製代碼

ActivityThread

private void handlePauseActivity(IBinder token, boolean finished,
            boolean userLeaving, int configChanges) {
        ActivityClientRecord r = mActivities.get(token);
        if (r != null) {
            ...
            performPauseActivity(token, finished, r.isPreHoneycomb());
            ...
            // Tell the activity manager we have paused.
            try {
                ActivityManagerNative.getDefault().activityPaused(token);
            } catch (RemoteException ex) {
            }
        }
    }
複製代碼

AMS收到ActivityA發送過來的pause消息以後,就會喚起ActivityB,入口仍是resumeTopActivityLocked,喚醒B,以後還會A給進一步stop掉,這個時候就牽扯到現場的保存,

ActivityStack

private final void completePauseLocked() {
   
    if (!mService.isSleeping()) {
        resumeTopActivityLocked(prev);
    } else {
    
   ...
}    ActivityB如何啓動的,本文不關心,只看ActivityA如何保存現場的,ActivityB起來後,會經過ActivityStack的stopActivityLocked去stop ActivityA,

private final void stopActivityLocked(ActivityRecord r) {
       ...
        if (mMainStack) {
             
            r.app.thread.scheduleStopActivity(r.appToken, r.visible, r.configChangeFlags);
        ...
       }    
複製代碼

回看APP端,看一下ActivityThread中的調用:首先經過callActivityOnSaveInstanceState,將現場保存到Bundle中去,

private void performStopActivityInner(ActivityClientRecord r,
        StopInfo info, boolean keepShown, boolean saveState) {
       ...
        // Next have the activity save its current state and managed dialogs...
        if (!r.activity.mFinished && saveState) {
            if (r.state == null) {
                state = new Bundle();
                state.setAllowFds(false);
                mInstrumentation.callActivityOnSaveInstanceState(r.activity, state);
                r.state = state;
         。。。
         }
複製代碼

以後,經過ActivityManagerNative.getDefault().activityStopped,通知AMS Stop動做完成,在通知的時候,還會將保存的現場數據帶過去。

private static class StopInfo implements Runnable {
    ActivityClientRecord activity;
    Bundle state;
    Bitmap thumbnail;
    CharSequence description;

    @Override public void run() {
        // Tell activity manager we have been stopped.
        try {
   
            ActivityManagerNative.getDefault().activityStopped(
                activity.token, state, thumbnail, description);
        } catch (RemoteException ex) {
        }
    }
}
複製代碼

經過上面流程,AMS不只啓動了新的Activity,同時也將上一個Activity的現場進行了保存,及時因爲種種緣由上一個Actiivity被殺死,在回退,或者從新喚醒的過程當中AMS也能知道如何喚起Activiyt,並恢復。

如今解決兩個問題,一、如何保存現場,二、AMS怎麼判斷知道APP或者Activity是否被異常殺死,那麼就剩下最後一個問題了,AMS如何恢復被異常殺死的APP或者Activity呢。

Activity或者Application恢復流程

Application被後臺殺死

其實在講解AMS怎麼判斷知道APP或者Activity是否被異常殺死的時候,就已經涉及了恢復的邏輯,也知道了一旦AMS知道了APP被後臺殺死了,那就不是正常的resuem流程了,而是要從新laucher,先來看一下整個APP被幹掉的會怎麼處理,看resumeTopActivityLocked部分,從上面的分析已知,這種場景下,會由於Binder通訊拋異常走異常分支,以下:

final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
  ....
  if (next.app != null && next.app.thread != null) {
            if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next);
            ...            
            try {
             ...
            } catch (Exception e) {
                // Whoops, need to restart this activity!
                這裏是知道整個app被殺死的
                Slog.i(TAG, "Restarting because process died: " + next);
                next.state = lastState;
                mResumedActivity = lastResumedActivity;
                Slog.i(TAG, "Restarting because process died: " + next);
              
                startSpecificActivityLocked(next, true, false);
                return true;
            }
複製代碼

從上面的代碼能夠知道,其實就是走startSpecificActivityLocked,這根第一次從桌面喚起APP沒多大區別,只是有一點須要注意,那就是這種時候啓動的Activity是有上一次的現場數據傳遞過得去的,由於上次在退到後臺的時候,全部Activity界面的現場都是被保存了,而且傳遞到AMS中去的,那麼此次的恢復啓動就會將這些數據返回給ActivityThread,再來仔細看一下performLaunchActivity裏面關於恢復的特殊處理代碼:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
 

    ActivityInfo aInfo = r.activityInfo;
     Activity activity = null;
    try {
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
    } catch (Exception e) {
     ...
    }
     try {
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            ...
             關鍵點 1 
            mInstrumentation.callActivityOnCreate(activity, r.state);
            ...
            r.activity = activity;
            r.stopped = true;
            if (!r.activity.mFinished) {
                activity.performStart();
                r.stopped = false;
            }
            關鍵點 1 
            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);
            ...
  
}
複製代碼

看一下關鍵點1跟2,先看關鍵點1,mInstrumentation.callActivityOnCreate會回調Actiivyt的onCreate,這個函數裏面其實主要針對FragmentActivity作一些Fragment恢復的工做,ActivityClientRecord中的r.state是AMS傳給APP用來恢復現場的,正常啓動的時候,這些都是null。再來看關鍵點2 ,在r.state != null非空的時候執行mInstrumentation.callActivityOnRestoreInstanceState,這個函數默認主要就是針對Window作一些恢復工做,好比ViewPager恢復以前的顯示位置等,也能夠用來恢復用戶保存數據。

Application沒有被後臺殺死

打開開發者模式」不保留活動「,就是這種場景,在上面的分析中,知道,AMS主動異常殺死Activity的時候,將AcitivityRecord的app字段置空,所以resumeTopActivityLocked同整個APP被殺死不一樣,會走下面的分支

final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
     ...
    	
    if (next.app != null && next.app.thread != null) { 
		...
		
    } else {
    		關鍵點 1 只是重啓Activity,可見這裏實際上是知道的,進程並沒死,
        // Whoops, need to restart this activity!
        
        startSpecificActivityLocked(next, true, true);
    }

    return true;
}
複製代碼

雖然不太同樣,可是一樣走startSpecificActivityLocked流程,只是不新建APP進程,其他的都是同樣的,再也不講解。

主動清除最近任務跟異常殺死的區別:ActivityStack是否正常清除

恢復的時候,爲何是倒序恢復:由於這是ActivityStack中的HistoryRecord中棧的順序,嚴格按照AMS端來

一句話歸納Android後臺殺死恢復原理:Application進程被Kill,但現場被AMS 保存,AMS能根據保存恢復Application現場

LowMemoryKiller原理

LowMemoryKiller(低內存殺手)是Andorid基於oomKiller原理所擴展的一個多層次oomKiller,OOMkiller(Out Of Memory Killer)是在Linux系統沒法分配新內存的時候,選擇性殺掉進程,到oom的時候,系統可能已經不太穩定,而LowMemoryKiller是一種根據內存閾值級別觸發的內存回收的機制,在系統可用內存較低時,就會選擇性殺死進程的策略,相對OOMKiller,更加靈活。

  • 進程優先級定義:只有有了優先級,才能決定先殺誰,後殺誰
  • 進程優先級的動態管理:一個進程的優先級不該該是固定不變的,須要根據其變更而動態變化,好比前臺進程切換到後臺優先級確定要下降
  • 進程殺死的時機,何時須要挑一個,或者挑多個進程殺死
  • 如何殺死

Android底層採用的是Linux內核,其進程管理都是基於Linux內核,LowMemoryKiller也相應的放在內核模塊,這也意味着用戶空間對於後臺殺死不可見,就像AMS徹底不知道一個APP是否被後臺殺死,只有在AMS喚醒APP的時候,才知道APP是否被LowMemoryKiller殺死過。其實LowmemoryKiller的原理是很清晰的,先看一下總體流程圖,再分步分析:

先記住兩點 :

  • LowMemoryKiller是被動殺死進程
  • Android應用經過AMS,利用proc文件系統更新進程信息

Android應用進程優先級及oomAdj

Android會盡量長時間地保持應用存活,但爲了新建或運行更重要的進程,可能須要移除舊進程來回收內存,在選擇要Kill的進程的時候,系統會根據進程的運行狀態做出評估,權衡進程的「重要性「,其權衡的依據主要是四大組件。若是須要縮減內存,系統會首先消除重要性最低的進程,而後是重要性略遜的進程,依此類推,以回收系統資源。在Android中,應用進程劃分5級(摘自Google文檔):Android中APP的重要性層次一共5級:

  • 前臺進程(Foreground process)
  • 可見進程(Visible process)
  • 服務進程(Service process)
  • 後臺進程(Background process)
  • 空進程(Empty process)

前臺進程

用戶當前操做所必需的進程。若是一個進程知足如下任一條件,即視爲前臺進程:

  • 包含正在交互的Activity(resumed
  • 包含綁定到正在交互的Activity的Service
  • 包含正在「前臺」運行的Service(服務已調用startForeground())
  • 包含正執行一個生命週期回調的Service(onCreate()、onStart() 或 onDestroy())
  • 包含一個正執行其onReceive()方法的BroadcastReceiver

一般,在任意給定時間前臺進程都爲數很少。只有在內存不足以支持它們同時繼續運行這一萬不得已的狀況下,系統纔會終止它們。 此時,設備每每已達到內存分頁狀態,所以須要終止一些前臺進程來確保用戶界面正常響應。

可見進程

沒有任何前臺組件、但仍會影響用戶在屏幕上所見內容的進程。 若是一個進程知足如下任一條件,即視爲可見進程:

  • 包含不在前臺、但仍對用戶可見的 Activity(已調用其 onPause() 方法)。例如,若是前臺 Activity 啓動了一個對話框,容許在其後顯示上一Activity,則有可能會發生這種狀況。
  • 包含綁定到可見(或前臺)Activity 的 Service。

可見進程被視爲是極其重要的進程,除非爲了維持全部前臺進程同時運行而必須終止,不然系統不會終止這些進程。

服務進程

正在運行已使用 startService() 方法啓動的服務且不屬於上述兩個更高類別進程的進程。儘管服務進程與用戶所見內容沒有直接關聯,可是它們一般在執行一些用戶關心的操做(例如,在後臺播放音樂或從網絡下載數據)。所以,除非內存不足以維持全部前臺進程和可見進程同時運行,不然系統會讓服務進程保持運行狀態。

後臺進程

包含目前對用戶不可見的 Activity 的進程(已調用 Activity 的 onStop() 方法)。這些進程對用戶體驗沒有直接影響,系統可能隨時終止它們,以回收內存供前臺進程、可見進程或服務進程使用。 一般會有不少後臺進程在運行,所以它們會保存在 LRU (最近最少使用)列表中,以確保包含用戶最近查看的 Activity 的進程最後一個被終止。若是某個 Activity 正確實現了生命週期方法,並保存了其當前狀態,則終止其進程不會對用戶體驗產生明顯影響,由於當用戶導航回該 Activity 時,Activity會恢復其全部可見狀態。

空進程

不含任何活動應用組件的進程。保留這種進程的的惟一目的是用做緩存,以縮短下次在其中運行組件所需的啓動時間,這就是所謂**熱啓動 **。爲了使系統資源在進程緩存和底層內核緩存之間保持平衡,系統每每會終止這些進程。

根據進程中當前活動組件的重要程度,Android會將進程評定爲它可能達到的最高級別。例如,若是某進程託管着服務和可見 Activity,則會將此進程評定爲可見進程,而不是服務進程。此外,一個進程的級別可能會因其餘進程對它的依賴而有所提升,即服務於另外一進程的進程其級別永遠不會低於其所服務的進程。 例如,若是進程 A 中的內容提供程序爲進程 B 中的客戶端提供服務,或者若是進程 A 中的服務綁定到進程 B 中的組件,則進程 A 始終被視爲至少與進程B一樣重要。

經過Google文檔,對不一樣進程的重要程度有了一個直觀的認識,下面看一下量化到內存是什麼樣的呈現形式,這裏針對不一樣的重要程度,作了進一步的細分,定義了重要級別ADJ,並將優先級存儲到內核空間的進程結構體中去,供LowmemoryKiller參考:

以上介紹的目的只有一點:Android的應用進程是有優先級的,它的優先級跟當前是否存在展現界面,以及是否能被用戶感知有關,越是被用戶感知的的應用優先級越高(系統進程不考慮)。

Android應用的優先級是如何更新的

APP中不少操做均可能會影響進程列表的優先級,好比退到後臺、移到前臺等,都會潛在的影響進程的優先級,咱們知道Lowmemorykiller是經過遍歷內核的進程結構體隊列,選擇優先級低的殺死,那麼APP操做是如何寫入到內核空間的呢?Linxu有用戶間跟內核空間的區分,不管是APP仍是系統服務,都是運行在用戶空間,嚴格說用戶控件的操做是沒法直接影響內核空間的,更不用說更改進程的優先級。其實這裏是經過了Linux中的一個proc文件體統,proc文件系統能夠簡單的看可能是內核空間映射成用戶能夠操做的文件系統,固然不是全部進程都有權利操做,經過proc文件系統,用戶空間的進程就可以修改內核空間的數據,好比修改進程的優先級,在Android家族,5.0以前的系統是AMS進程直接修改的,5.0以後,是修改優先級的操做被封裝成了一個獨立的服務-lmkd,lmkd服務位於用戶空間,其做用層次同AMS、WMS相似,就是一個普通的系統服務。咱們先看一下5.0以前的代碼,這裏仍然用4.3的源碼看一下,模擬一個場景,APP只有一個Activity,咱們主動finish掉這個Activity,APP就回到了後臺,這裏要記住,雖然沒有可用的Activity,可是APP自己是沒喲死掉的,這就是所謂的熱啓動,先看下大致的流程:

ActivityManagerService

public final boolean finishActivity(IBinder token, int resultCode, Intent resultData) {
     ...
    synchronized(this) {
       
        final long origId = Binder.clearCallingIdentity();
        boolean res = mMainStack.requestFinishActivityLocked(token, resultCode,
                resultData, "app-request", true);
     ...
    }
}
複製代碼

一開始的流程跟startActivity相似,首先是先暫停當前resume的Activity,其實也就是本身,

final boolean finishActivityLocked(ActivityRecord r, int index, int resultCode,
            Intent resultData, String reason, boolean immediate, boolean oomAdj) {
         ...
            if (mPausingActivity == null) {
                if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r);
                if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false");
                startPausingLocked(false, false);
            }
			...
    }
複製代碼

pause掉當前Activity以後,還須要喚醒上一個Activity,若是當前APP的Activity棧裏應經空了,就回退到上一個應用或者桌面程序,喚醒流程就不在講解了,由於在AMS恢復異常殺死APP的那篇已經說過,這裏要說的是喚醒以後對這個即將退回後臺的APP的操做,這裏注意與startActivity不一樣的地方,看下面代碼:

ActivityStack

private final void completePauseLocked() {
    ActivityRecord prev = mPausingActivity;
     
    if (prev != null) {
        if (prev.finishing) {
        一、 不一樣點
       <!--主動finish的時候,走的是這個分支,狀態變換的細節請本身查詢代碼-->
            prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false);
        } 
        ...
		二、相同點 		
     if (!mService.isSleeping()) {
        resumeTopActivityLocked(prev);
    }
複製代碼

看一下上面的兩個關鍵點1跟2,1是同startActivity的completePauseLocked不一樣的地方,主動finish的prev.finishing是爲true的,所以會執行finishCurrentActivityLocked分支,將當前pause的Activity加到mStoppingActivities隊列中去,而且喚醒下一個須要走到到前臺的Activity,喚醒後,會繼續執行stop:

private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,
	        int index, int mode, boolean oomAdj) {
	    if (mode == FINISH_AFTER_VISIBLE && r.nowVisible) {
	        if (!mStoppingActivities.contains(r)) {
	            mStoppingActivities.add(r);
	 			...
	        }
			   ....
	        return r;
	    }
		...
	}
複製代碼

讓咱們再回到resumeTopActivityLocked繼續看,resume以後會回調completeResumeLocked函數,繼續執行stop,這個函數經過向Handler發送IDLE_TIMEOUT_MSG消息來回調activityIdleInternal函數,最終執行destroyActivityLocked銷燬ActivityRecord,

final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
		...
   if (next.app != null && next.app.thread != null) {					...
            try {
            	。。。
                next.app.thread.scheduleResumeActivity(next.appToken,
                        mService.isNextTransitionForward());
      			..。
            try {
                next.visible = true;
                completeResumeLocked(next);
            }  
            ....
         } 
複製代碼

在銷燬Activity的時候,若是當前APP的Activity堆棧爲空了,就說明當前Activity沒有可見界面了,這個時候就須要動態更新這個APP的優先級,詳細代碼以下:

final boolean destroyActivityLocked(ActivityRecord r,
            boolean removeFromApp, boolean oomAdj, String reason) {
    		...
       if (hadApp) {
            if (removeFromApp) {
                // 這裏動ProcessRecord裏面刪除,可是沒從history刪除
                int idx = r.app.activities.indexOf(r);
                if (idx >= 0) {
                    r.app.activities.remove(idx);
                }
                ...
                if (r.app.activities.size() == 0) {
                    // No longer have activities, so update oom adj.
                    mService.updateOomAdjLocked();
             	...
       }
複製代碼

最終會調用AMS的updateOomAdjLocked函數去更新進程優先級,在4.3的源碼裏面,主要是經過Process類的setOomAdj函數來設置優先級:

ActivityManagerService

private final boolean updateOomAdjLocked(ProcessRecord app, int hiddenAdj,
        int clientHiddenAdj, int emptyAdj, ProcessRecord TOP_APP, boolean doingAll) {
    ...
    計算優先級
    computeOomAdjLocked(app, hiddenAdj, clientHiddenAdj, emptyAdj, TOP_APP, false, doingAll);
	 。。。
 		 <!--若是不相同,設置新的OomAdj-->
  		 
    if (app.curAdj != app.setAdj) {
        if (Process.setOomAdj(app.pid, app.curAdj)) {
        ...
}
複製代碼

Process中setOomAdj是一個native方法,原型在android_util_Process.cpp中

android_util_Process.cpp

jboolean android_os_Process_setOomAdj(JNIEnv* env, jobject clazz,
                                      jint pid, jint adj)
{
#ifdef HAVE_OOM_ADJ
    char text[64];
    sprintf(text, "/proc/%d/oom_adj", pid);
    int fd = open(text, O_WRONLY);
    if (fd >= 0) {
        sprintf(text, "%d", adj);
        write(fd, text, strlen(text));
        close(fd);
    }
    return true;
#endif
    return false;
}
複製代碼

能夠看到,在native代碼裏,就是經過proc文件系統修改內核信息,這裏就是動態更新進程的優先級oomAdj,以上是針對Android4.3系統的分析,以後會看一下5.0以後的系統是如何實現的。下面是4.3更新oomAdj的流程圖,注意紅色的執行點:

Android5.0以後框架層的實現:LMKD服務

Android5.0將設置進程優先級的入口封裝成了一個獨立的服務lmkd服務,AMS再也不直接訪問proc文件系統,而是經過lmkd服務來進行設置,從init.rc文件中看到服務的配置。

service lmkd /system/bin/lmkd
    class core
    critical
    socket lmkd seqpacket 0660 system system
複製代碼

從配置中能夠看出,該服務是經過socket與其餘進行進程進行通訊,其實就是AMS經過socket向lmkd服務發送請求,讓lmkd去更新進程的優先級,lmkd收到請求後,會經過/proc文件系統去更新內核中的進程優先級。首先看一下5.0中這一塊AMS有什麼改變,其實大部分流程跟以前4.3源碼相似,咱們只看一下不一樣地方

ActivityManagerService

private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
        ProcessRecord TOP_APP, boolean doingAll, long now) {
    ...
    computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now);
    ...
    applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
}

private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
        long nowElapsed) {
    boolean success = true;

    if (app.curRawAdj != app.setRawAdj) {
        app.setRawAdj = app.curRawAdj;
    }

    int changes = 0;
	  不一樣點1
    if (app.curAdj != app.setAdj) {
        ProcessList.setOomAdj(app.pid, app.info.uid, app.curAdj);
        if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
                "Set " + app.pid + " " + app.processName + " adj " + app.curAdj + ": "
                + app.adjType);
        app.setAdj = app.curAdj;
        app.verifiedAdj = ProcessList.INVALID_ADJ;
    }
複製代碼

從上面的不一樣點1能夠看出,5.0以後是經過ProcessList類去設置oomAdj,其實這裏就是經過socket與LMKD服務進行通訊,向lmkd服務傳遞給LMK_PROCPRIO命令去更新進程優先級:

public static final void setOomAdj(int pid, int uid, int amt) {
    if (amt == UNKNOWN_ADJ)
        return;
   long start = SystemClock.elapsedRealtime();
    ByteBuffer buf = ByteBuffer.allocate(4 * 4);
    buf.putInt(LMK_PROCPRIO);
    buf.putInt(pid);
    buf.putInt(uid);
    buf.putInt(amt);
    writeLmkd(buf);
    long now = SystemClock.elapsedRealtime();
      }    
    
private static void writeLmkd(ByteBuffer buf) {
	 	for (int i = 0; i < 3; i++) {
	    if (sLmkdSocket == null) {
	      if (openLmkdSocket() == false) {
			...
	    try {
	        sLmkdOutputStream.write(buf.array(), 0, buf.position());
	        return;
	        ...
	}
複製代碼

其實就是openLmkdSocket打開本地socket端口,並將優先級信息發送過去,那麼lmkd服務端如何處理的呢,init.rc裏配置的服務是在開機時啓動的,來看看lmkd服務的入口:main函數

lmkd.c函數

int main(int argc __unused, char **argv __unused) {
    struct sched_param param = {
            .sched_priority = 1,
    };

    mlockall(MCL_FUTURE);
    sched_setscheduler(0, SCHED_FIFO, &param);
    if (!init())
        mainloop();

    ALOGI("exiting");
    return 0;
}
複製代碼

很簡單,打開一個端口,並經過mainloop監聽socket,若是有請求到來,就解析命令並執行,剛纔傳入的LMK_PROCPRIO命令對應的操做就是cmd_procprio,用來更新oomAdj,其更新新機制仍是經過proc文件系統,不信?看下面代碼:

static void cmd_procprio(int pid, int uid, int oomadj) {
    struct proc *procp;
    。。。
    仍是利用/proc文件系統進行更新
    snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid);
    snprintf(val, sizeof(val), "%d", lowmem_oom_adj_to_oom_score_adj(oomadj));
    writefilestring(path, val);
   。。。
}
複製代碼

簡單的流程圖以下,同4.3不一樣的地方

LomemoryKiller內核部分:如何殺死

LomemoryKiller屬於一個內核驅動模塊,主要功能是:在系統內存不足的時候掃描進程隊列,找到低優先級(也許說性價比低更合適)的進程並殺死,以達到釋放內存的目的。對於驅動程序,入口是__init函數,先看一下這個驅動模塊的入口:

static int __init lowmem_init(void)
{
	register_shrinker(&lowmem_shrinker);
	return 0;
}
複製代碼

能夠看到在init的時候,LomemoryKiller將本身的lowmem_shrinker入口註冊到系統的內存檢測模塊去,做用就是在內存不足的時候能夠被回調,register_shrinker函數是一屬於另外一個內存管理模塊的函數,若是必定要根下去的話,能夠看一下它的定義,其實就是加到一個回調函數隊列中去:

void register_shrinker(struct shrinker *shrinker)
{
	shrinker->nr = 0;
	down_write(&shrinker_rwsem);
	list_add_tail(&shrinker->list, &shrinker_list);
	up_write(&shrinker_rwsem);
}
複製代碼

最後,看一下,當內存不足觸發回調的時候,LomemoryKiller是如何找到低優先級進程,並殺死的:入口函數就是init時候註冊的lowmem_shrink函數(4.3源碼,後面的都有微調但原理大概相似):

static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask)
{
	struct task_struct *p;
	。。。
	關鍵點1 找到當前的內存對應的閾值
	for(i = 0; i < array_size; i++) {
		if (other_free < lowmem_minfree[i] &&
		    other_file < lowmem_minfree[i]) {
			min_adj = lowmem_adj[i];
			break;
		}
	}
	。。。
	關鍵點2 找到優先級低於這個閾值的進程,並殺死
	
	read_lock(&tasklist_lock);
	for_each_process(p) {
		if (p->oomkilladj < min_adj || !p->mm)
			continue;
		tasksize = get_mm_rss(p->mm);
		if (tasksize <= 0)
			continue;
		if (selected) {
			if (p->oomkilladj < selected->oomkilladj)
				continue;
			if (p->oomkilladj == selected->oomkilladj &&
			    tasksize <= selected_tasksize)
				continue;
		}
		selected = p;
		selected_tasksize = tasksize;
 
	}
	if(selected != NULL) {
		force_sig(SIGKILL, selected);
		rem -= selected_tasksize;
	}
	lowmem_print(4, "lowmem_shrink %d, %x, return %d\n", nr_to_scan, gfp_mask, rem);
	read_unlock(&tasklist_lock);
	return rem;
}
複製代碼

先看關鍵點1:其實就是肯定當前低內存對應的閾值;關鍵點2 :找到比該閾值優先級低或者相等,而且內存佔用較多的進程(tasksize = get_mm_rss(p->mm)其實就是獲取內存佔用)),將其殺死。如何殺死的呢?很直接,經過Linux的中的信號量,發送SIGKILL信號直接將進程殺死。到這就分析完了LomemoryKiller內核部分如何工做的。其實很簡單,一句話:被動掃描,找到低優先級的進程,殺死。

  • Android APP進程是有優先級的的,與進程是否被用戶感知有直接關係
  • APP切換等活動均可能形成進程優先級的變化,都是利用AMS,並經過proc文件設置到內核的
  • LowmemoryKiller運行在內核,在內存須要縮減的時候,會選擇低優先級的進程殺死

Binder訃告原理

Binder是一個相似於C/S架構的通訊框架,有時候客戶端可能想知道服務端的狀態,好比服務端若是掛了,客戶端但願能及時的被通知到,而不是等到再起請求服務端的時候才知道,這種場景其實在互爲C/S的時候最經常使用,好比AMS與APP,當APP端進程異常退出的時候,AMS但願能及時知道,不只僅是清理APP端在AMS中的一些信息,好比ActivityRecord,ServiceRecord等,有時候可能還須要及時恢復一些自啓動的Service。Binder實現了一套」死亡訃告」的功能,即:服務端掛了,或者正常退出,Binder驅動會向客戶端發送一份訃告,告訴客戶端Binder服務掛了。

Binder「訃告」有點採用了相似觀察者模式,所以,首先須要將Observer註冊到目標對象中,其實就是將Client註冊到Binder驅動,未來Binder服務掛掉時候,就能經過驅動去發送。Binder「訃告」發送的入口只有一個:在釋放binder設備的時候,在在操做系統中,不管進程是正常退出仍是異常退出,進程所申請的全部資源都會被回收,包括打開的一些設備文件,如Binder字符設備等。在釋放的時候,就會調用相應的release函數,「訃告」也就是在這個時候去發送的。所以Binder訃告其實就僅僅包括兩部分:註冊與通知。

Binder」訃告」的註冊入口

這裏拿bindService爲例子進行分析,其餘場景相似,bindService會首先請求AMS去啓動Service,Server端進程在啓動時,會調用函數open來打開設備文件/dev/binder,同時將Binder服務實體回傳給AMS,AMS再將Binder實體的引用句柄經過Binder通訊傳遞給Client,也就是在AMS回傳給Client的時候,會向Binder驅動註冊。其實這也比較好理解,得到了服務端的代理,就應該關心服務端的死活 。當AMS利用IServiceConnection這條binder通訊線路爲Client回傳Binder服務實體的時候,InnerConnection就會間接的將死亡回調註冊到內核:

private static class InnerConnection extends IServiceConnection.Stub {
        final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;

        public void connected(ComponentName name, IBinder service) throws RemoteException {
            LoadedApk.ServiceDispatcher sd = mDispatcher.get();
            if (sd != null) {
                sd.connected(name, service);
            }
        }
    }
複製代碼

ServiceDispatcher函數進一步調用 doConnected

public void doConnected(ComponentName name, IBinder service) {
    ServiceDispatcher.ConnectionInfo old;
    ServiceDispatcher.ConnectionInfo info;
    synchronized (this) {     
        if (service != null) {
            mDied = false;
            info = new ConnectionInfo();
            info.binder = service;
            info.deathMonitor = new DeathMonitor(name, service);
            try {
            <!-- 關鍵點點1-->
                service.linkToDeath(info.deathMonitor, 0);
            } 
}
複製代碼

看關鍵點點1 ,這裏的IBinder service實際上是AMS回傳的服務代理BinderProxy,linkToDeath是一個Native函數,會進一步調用BpBinde的linkToDeath:

status_t BpBinder::linkToDeath(
    const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags){
    <!--關鍵點1-->              
                IPCThreadState* self = IPCThreadState::self();
                self->requestDeathNotification(mHandle, this);
                self->flushCommands();

}
複製代碼

最終調用IPCThreadState的requestDeathNotification(mHandle, this)向內核發送BC_REQUEST_DEATH_NOTIFICATION請求:

status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy)
{
    mOut.writeInt32(BC_REQUEST_DEATH_NOTIFICATION);
    mOut.writeInt32((int32_t)handle);
    mOut.writeInt32((int32_t)proxy);
    return NO_ERROR;
}
複製代碼

最後來看一下在內核中,是怎麼登記註冊的:

int
binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
		    void __user *buffer, int size, signed long *consumed)
{
...
case BC_REQUEST_DEATH_NOTIFICATION:
		case BC_CLEAR_DEATH_NOTIFICATION: {
			...
			ref = binder_get_ref(proc, target);
			if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {
				...關鍵點1
				death = kzalloc(sizeof(*death), GFP_KERNEL);
				binder_stats.obj_created[BINDER_STAT_DEATH]++;
				INIT_LIST_HEAD(&death->work.entry);
				death->cookie = cookie;
				ref->death = death;
				if (ref->node->proc == NULL) {
					ref->death->work.type = BINDER_WORK_DEAD_BINDER;
					if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
						list_add_tail(&ref->death->work.entry, &thread->todo);
					} else {
						list_add_tail(&ref->death->work.entry, &proc->todo);
						wake_up_interruptible(&proc->wait);
					}
				}
			} 
 }
複製代碼

看關鍵點1 ,其實就是爲Client新建binder_ref_death對象,並賦值給binder_ref。在binder驅動中,binder_node節點會記錄全部binder_ref,當binder_node所在的進程掛掉後,驅動就能根據這個全局binder_ref列表找到全部Client的binder_ref,並對於設置了死亡回調的Client發送「訃告」,這是由於在binder_get_ref_for_node向Client插入binder_ref的時候,也會插入binder_node的binder_ref列表。

static struct binder_ref *
binder_get_ref_for_node(struct binder_proc *proc, struct binder_node *node)
{
	struct rb_node *n;
	struct rb_node **p = &proc->refs_by_node.rb_node;
	struct rb_node *parent = NULL;
	struct binder_ref *ref, *new_ref;

	if (node) {
		hlist_add_head(&new_ref->node_entry, &node->refs);
		}
	return new_ref;
}
複製代碼

如此,死亡回調入口就被註冊到binder內核驅動,以後,等到進程結束要釋放binder的時候,就會觸發死亡回調。

死亡通知的發送

在調用binder_realease函數來釋放相應資源的時候,最終會調用binder_deferred_release函數。該函數會遍歷該binder_proc內全部的binder_node節點,並向註冊了死亡回調的Client發送訃告,

static void binder_deferred_release(struct binder_proc *proc)
	{         ....
		if (ref->death) {
				death++;
				if (list_empty(&ref->death->work.entry)) {
					ref->death->work.type = BINDER_WORK_DEAD_BINDER;
					list_add_tail(&ref->death->work.entry, &ref->proc->todo);
					// 插入到binder_ref請求進程的binder線程等待隊列????? 自然支持binder通訊嗎?
					// 何時,須要死亡回調,本身也是binder服務?
					wake_up_interruptible(&ref->proc->wait);
				} 				
		...
 }
複製代碼

死亡訃告被直接發送到Client端的binder進程todo隊列上,這裏彷佛也只對於互爲C/S通訊的場景有用,當Client的binder線程被喚醒後,就會針對「訃告」作一些清理及善後工做:

static int
binder_thread_read(struct binder_proc *proc, struct binder_thread *thread,
	void  __user *buffer, int size, signed long *consumed, int non_block)
	{
		case BINDER_WORK_DEAD_BINDER:
				case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
				case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {
					struct binder_ref_death *death = container_of(w, struct binder_ref_death, work);
					uint32_t cmd;
					if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION)
						cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE;
					else
						cmd = BR_DEAD_BINDER;
					...
 }
複製代碼

這裏會向用戶空間寫入一個BR_DEAD_BINDER命令,並返回talkWithDriver函數,返回後,IPCThreadState會繼續執行executeCommand,

status_t IPCThreadState::executeCommand(int32_t cmd)
{
    // 死亡訃告
    case BR_DEAD_BINDER:
        {
            BpBinder *proxy = (BpBinder*)mIn.readInt32();
            <!--關鍵點1 -->
            proxy->sendObituary();
            mOut.writeInt32(BC_DEAD_BINDER_DONE);
            mOut.writeInt32((int32_t)proxy);
        } break;   }
複製代碼

看關鍵點1,Obituary直譯過來就是訃告,其實就是利用BpBinder發送訃告,待訃告處理結束後,再向Binder驅動發送確認通知。

void BpBinder::sendObituary()
{
    ALOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n",
        this, mHandle, mObitsSent ? "true" : "false");
    mAlive = 0;
    if (mObitsSent) return;
    mLock.lock();
    Vector<Obituary>* obits = mObituaries;
    if(obits != NULL) {
    <!--關鍵點1-->
        IPCThreadState* self = IPCThreadState::self();
        self->clearDeathNotification(mHandle, this);
        self->flushCommands();
        mObituaries = NULL;
    }
    mObitsSent = 1;
    mLock.unlock();
    if (obits != NULL) {
        const size_t N = obits->size();
        for (size_t i=0; i<N; i++) {
            reportOneDeath(obits->itemAt(i));
        }
        delete obits;
    }
}
複製代碼

看關鍵點1,這裏跟註冊相對應,將本身從觀察者列表中清除,以後再上報

void BpBinder::reportOneDeath(const Obituary& obit)
{
    sp<DeathRecipient> recipient = obit.recipient.promote();
    ALOGV("Reporting death to recipient: %p\n", recipient.get());
    if (recipient == NULL) return;

    recipient->binderDied(this);
}
複製代碼

進而調用上層DeathRecipient的回調,作一些清理之類的邏輯。以AMS爲例,其binderDied函數就挺複雜,包括了一些數據的清理,甚至還有進程的重建等,不作討論。

相關文章
相關標籤/搜索