1. 程式人生 > >Android之什麼時候呼叫onSaveInstance方法的時候(為什麼按Home鍵盤會呼叫,按Back不呼叫)

Android之什麼時候呼叫onSaveInstance方法的時候(為什麼按Home鍵盤會呼叫,按Back不呼叫)

1、函式介紹

1)、onCreate(Bundle savedInstanceState) 方法

Activity 建立時回撥 : 該方法會自動傳入一個 Bundle 物件, 該 Bundle 物件就是上次被系統銷燬時在 onSaveInstanceState 或者 onRestoreInstanceState 中儲存的資料
-- 注意 : 只有是系統自動回收的時候才會儲存 Bundle 物件資料;
-- Bundle 物件來源 : onCreate() 方法中的 Bundle 物件引數, 是在 onSaveInstance() 或者 onRestoreInstanceState() 方法中儲存的 Bundle 物件;

2)、 onSaveInstanceState(Bundle outState) 方法

onSaveInstanceState函式是Activity的生命週期函式
outState 引數作用 :
 資料儲存 : Activity 宣告週期結束的時候, 需要儲存 Activity 狀態的時候, 會將要儲存的資料使用鍵值對的形式 儲存在 Bundle 物件中;
 恢復資料 : 在 Activity 的 onCreate()方法 建立 Activity 的時候會傳入一個 Bundle 物件, 這個 Bundle 物件就是這個 outState 引數;


呼叫時機 : Activity 容易被銷燬的時候呼叫, 注意是容易被銷燬, 也可能沒有銷燬就呼叫了; 按下Home鍵 : Activity 進入了後臺, 此時會呼叫該方法;
按下電源鍵 : 螢幕關閉, Activity 進入後臺;
啟動其它 Activity : Activity 被壓入了任務棧的棧底;
橫豎屏切換 : 會銷燬當前 Activity 並重新建立; onSaveInstanceState方法呼叫注意事項 :
 使用者主動銷燬不會呼叫 : 當用戶點選回退鍵 或者 呼叫了 finish() 方法, 不會呼叫該方法;
呼叫時機不固定 : 該方法一定是在 onStop() 方法之前呼叫, 但是不確定是在 onPause() 方法之前 還是 之後呼叫;
佈局中元件狀態儲存 : 每個元件都 實現了 onSaveInstance() 方法, 在呼叫函式的時候, 會自動儲存元件的狀態, 注意, 只有有 id 的元件才會儲存;
關於預設的 super.onSaveInstanceState(outState) : 該預設的方法是實現 元件狀態儲存的;

(3) onRestoreInstanceState(Bundle savedInstanceState) 方法

方法回撥時機 : 在 Activity 被系統銷燬之後 恢復 Activity 時被呼叫, 只有銷燬了之後重建的時候才呼叫, 如果記憶體充足, 系統沒有銷燬這個 Activity, 就不需要呼叫;
-- Bundle 物件傳遞 : 該方法儲存的 Bundle 物件在 Activity 恢復的時候也會通過引數傳遞到 onCreate() 方法中;

2、原始碼分析呼叫onSaveInstance函式的時候

1)、看下ActivityThread.handlePauseActivity的原始碼
private void handlePauseActivity(IBinder token, boolean finished,
            boolean userLeaving, int configChanges, boolean dontReport) {
        ActivityClientRecord r = mActivities.get(token);
        if (r != null) {
            //Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);
            if (userLeaving) {
                performUserLeavingActivity(r);
            }

            r.activity.mConfigChangeFlags |= configChanges;
            performPauseActivity(token, finished, r.isPreHoneycomb());

            // Make sure any pending writes are now committed.
            if (r.isPreHoneycomb()) {
                QueuedWork.waitToFinish();
            }

            // Tell the activity manager we have paused.
            if (!dontReport) {
                try {
                    ActivityManagerNative.getDefault().activityPaused(token);
                } catch (RemoteException ex) {
                }
            }
            mSomeActivitiesChanged = true;
        }
    }

再看performPauseActivity方法
final Bundle performPauseActivity(IBinder token, boolean finished,
            boolean saveState) {
        ActivityClientRecord r = mActivities.get(token);
        return r != null ? performPauseActivity(r, finished, saveState) : null;
    }
再看掉用過載方法performPauseActivity
final Bundle performPauseActivity(ActivityClientRecord r, boolean finished,
            boolean saveState) {
        ...
        if (!r.activity.mFinished && saveState) {
            callCallActivityOnSaveInstanceState(r);
        }
        ...
    }
我們知道呼叫callCallActivityOnSaveInstanceState方法,看名稱發現這裡應該回調的是Activity的onSaveInstanceState方法,我們再看掉用這個函式的條件
!r.activity.mFinished && saveState
如果activity沒有掉用finish() 方法,mFinished的值就是false,如果需要進入這個函式,就需要後面的值saveState值為0,這裡的saveState是performPauseActivity方法傳遞過來的
performPauseActivity(token, finished, r.isPreHoneycomb());
我們再看函式r.isPreHoneycomb
public boolean isPreHoneycomb() {
            if (activity != null) {
                return activity.getApplicationInfo().targetSdkVersion
                        < android.os.Build.VERSION_CODES.HONEYCOMB;
            }
            return false;
}
App設定的targetSdk版本號小於android versionCode 11也就是android3.0的時候返回為true,其他的時候返回為false,也就是說當我們App設定的targetVersion大於android3.0的時候才會執行callCallActivityOnSaveInstanceState方法,然後接著看callCallActivityOnSaveInstanceState方法,如下
private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) {
        r.state = new Bundle();
        r.state.setAllowFds(false);
        if (r.isPersistable()) {
            r.persistentState = new PersistableBundle();
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
                    r.persistentState);
        } else {
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
        }
    }
我們再去Instrumentation.java裡面去看函式callActivityOnSaveInstanceState
public void callActivityOnSaveInstanceState(Activity activity, Bundle outState,
            PersistableBundle outPersistentState) {
        activity.performSaveInstanceState(outState, outPersistentState);
    }
我們再去Activity.java裡面去看函式performSaveInstanceState實現
final void performSaveInstanceState(Bundle outState) {
        onSaveInstanceState(outState);
        saveManagedDialogs(outState);
        mActivityTransitionState.saveState(outState);
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
    }
可以看到這裡掉用了Activity的onSaveInstanceState方法,這樣經過一系列的方法回撥之後就執行了onSaveInstanceState方法。
接下來我們看下onStop方法是否會執行onSaveInstanceState方法,同理Actvitiy執行onStop方法會回撥ActivityThread的handleStopActivity
接下來我們看handleStopActivity方法的實現:

private void handleStopActivity(IBinder token, boolean show, int configChanges) {
        ActivityClientRecord r = mActivities.get(token);
        r.activity.mConfigChangeFlags |= configChanges;

        StopInfo info = new StopInfo();
        performStopActivityInner(r, info, show, true);

        if (localLOGV) Slog.v(
            TAG, "Finishing stop of " + r + ": show=" + show
            + " win=" + r.window);

        updateVisibility(r, show);

        info.activity = r;
        info.state = r.state;
        info.persistentState = r.persistentState;
        mH.post(info);
        mSomeActivitiesChanged = true;
    }
我們再來看方法performStopActivityInner實現
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) {
                    callCallActivityOnSaveInstanceState(r);
                }
            }

            if (!keepShown) {
                try {
                    // Now we are idle.
                    r.activity.performStop();
                } catch (Exception e) {
                    if (!mInstrumentation.onException(r.activity, e)) {
                        throw new RuntimeException(
                                "Unable to stop activity "
                                + r.intent.getComponent().toShortString()
                                + ": " + e.toString(), e);
                    }
                }
                r.stopped = true;
            }


我們知道saveState穿進去為true,只要mFinished不是true就一定進入這個方法,所以只要在mFinished不為true,也就是沒有呼叫finish()函式的前提下,就一定執行onSaveInstanceState方法,所以當App設定的targetVersion大於android3.0,沒有呼叫finish函式的情況下,一定會呼叫onSaveInstanceState方法後再去呼叫onStop方法。

3、為什麼按Home鍵盤會掉用onSaveInstance方法儲存資料,按Back不掉用onSaveInstance方法儲存資料

因為按下Home鍵盤沒有呼叫 finish函式,如果targetVersion大於Androi3.0就一定執行onSaveInstanceState方法,所以就儲存資料了,如果按下返回鍵,會呼叫 finish方法,所有mFinished為true,所以不會掉用onSaveInstanceState方法,所以不會儲存資料。


4、手機常見操作Activity生命週期狀態變化來驗證

寫了一個簡單的Activity,在每個Activity周期函式裡面列印了相關的執行函式資訊

1)、啟動Activity


2)、按下電源或者Home鍵


3)、按亮電源鍵或者點選專案(一開啟按了Home鍵)


4)、按下返回鍵(back鍵盤)



5、總結

1、onSaveInstanceState方法是Activity的生命週期方法,主要用於在Activity銷燬時儲存一些資訊。

2、當Activity只執行onPause方法時(Activity a開啟一個透明Activity b)這時候如果App設定的targetVersion大於android3.0則不會執行onSaveInstanceState方法。

3、當Activity執行onStop方法時,通過分析原始碼我們知道只要Activity沒有執行finish函式一定會呼叫onSaveInstanceState的方法,然後再去掉用onStop方法。
onPause() ->  onSaveInstanceState() -> onStop()