AndroidでのonSaveInstanceState()解析


前言
前回の記事では、FragmentでCan not perform this action after onSaveInstanceStateの異常が発生したことを分析しました.onSaveInstanceStateの後でcommitの操作ができないことについて言及しています.ActivityのonSaveInstanceStateメソッドについて説明します.Android apiドキュメントでは、This method is called before an activity may be killed so that when it comes back some time in the future it can restore its state.For example, if activity B is launched in front of activity A, and at some point activity A is killed to reclaim resources, activity A will have a chance to save the current state of its user interface via this method so that when the user returns to activity A, the state of the user interface can be restored via onCreate(Bundle) or onRestoreInstanceState(Bundle). 上記の大まかな意味では、onSaveInstanceStateメソッドはactivityがkillされるときに実行され、将来的にはいつかその状態を回復することができます.例えば、AページでBページを起動し、ある時点でAがキルによってリソースを回収されると、Aはこの方法で現在のインタフェースの状態を保存する機会があり、これにより、ユーザがAページに戻ると、ユーザインタフェースの状態はonCreateおよびonRestoreInstancesState法によって回復することができる.onSaveInstanceStateメソッドをactivityのライフサイクルコールバックメソッドと混同しないでください.たとえば、onPause()は、activityがバックグラウンドで実行されたり破棄されたりしたときに呼び出されますが、必ずしもそうではありません.ここでは、onPause()とonStop()が呼び出されると、onSaveInstanceStateは呼び出されません.ユーザーはBページでbackキーを使用してAページに戻ります.ここでは、BページのonSaveInstanceStateメソッドを呼び出す必要はありません.このBページのインスタンスは永遠に復元されないため、システムはこのメソッドを呼び出すことを避けます.
いつonSaveInstanceStateを実行しますか?
1.activity Aから新しいactivityを起動する場合.2.画面方向切り替えの場合、例えば縦画面から横画面に切り替えた場合.注意:画面切り替えの前にactivity Aが破棄され、画面切り替え後にactivity Aが自動的に作成されるので、onSaveInstanceStateは必ず実行されます.3.電源ボタン(画面表示をOFF)を押した場合4.ユーザがHOMEキーを押してデスクトップに戻るとき.
要するに、onSaveInstanceStateの呼び出しは、システムが「許可されていない」ときにactivityを破棄すると、onSaveInstanceStateがシステムに呼び出されます.これはシステムの責任です.データを保存する機会を提供しなければならないので、積極的にfinish activityを保存する場合、この方法は呼び出されません.また、onSaveInstanceState()はonPause()またはonStop()の前に実行され、onRestoreInstanceState()はonStart()とonResume()の間で実行されます.
ソース解析
プロジェクトでは、AppCompatActivityクラスを継承しています.このクラスからonSaveInstanceStateメソッドの実行を表示できます.
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        getDelegate().onSaveInstanceState(outState);
    }

AppCompatActivityクラスは、FragmentActivityを継承し、親を表示し続ける方法です.次のようになります.
    /**
     * Save all appropriate fragment state.
     */
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
        if (mPendingFragmentActivityResults.size() > 0) {
            outState.putInt(NEXT_CANDIDATE_REQUEST_INDEX_TAG, mNextCandidateRequestIndex);

            int[] requestCodes = new int[mPendingFragmentActivityResults.size()];
            String[] fragmentWhos = new String[mPendingFragmentActivityResults.size()];
            for (int i = 0; i < mPendingFragmentActivityResults.size(); i++) {
                requestCodes[i] = mPendingFragmentActivityResults.keyAt(i);
                fragmentWhos[i] = mPendingFragmentActivityResults.valueAt(i);
            }
            outState.putIntArray(ALLOCATED_REQUEST_INDICIES_TAG, requestCodes);
            outState.putStringArray(REQUEST_FRAGMENT_WHO_TAG, fragmentWhos);
        }
    }

メソッドの内部から、FragmentManagerのmStateSaved=trueを制御するsaveAllState()が実行されていることがわかります.具体的には前の文章を見ることができます.そして、outStateにはFragmentの情報がいくつか保存されています.ActivityのonSaveInstanceStateメソッドの表示を続行します.次のようにします.
protected void onSaveInstanceState(Bundle outState) {
    outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
    Parcelable p = mFragments.saveAllState();
    if (p != null) {
        outState.putParcelable(FRAGMENTS_TAG, p);
    }
    getApplication().dispatchActivitySaveInstanceState(this, outState);
}

同様にsaveAllState()を実行する方法と,saveHierarchyState()の方法がある.一方,saveHierarchyState()メソッドでは,idなどいくつかの情報を含むUIの階層状態を主にwindowのHierarchyStateとして保存している.
画面方向の切り替えをまとめると、Activityがシステムによって破棄されて再確立され、onSaveInstanceStateメソッドを呼び出してデータを保存します.メソッドの実行手順は、onPause->onSaveInstanceState->onStop->onDestory->onCreate(画面を切り替えてActivityを再作成するときに呼び出されるonCreateメソッド)->onStart->onRestoryInstanceState->onResumeです.