<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                #### 1.1.2 異常情況下的生命周期分析 上一節我們分析了典型情況下Activity的生命周期,本節我們接著分析Activity在異常情況下的生命周期。我們知道,Activity除了受用戶操作所導致的正常的生命周期方法調度,還有一些異常情況,比如當資源相關的系統配置發生改變以及系統內存不足時,Activity就可能被殺死。下面我們具體分析這兩種情況。 **1.情況1:資源相關的系統配置發生改變導致Activity被殺死并重新創建** 理解這個問題,我們首先要對系統的資源加載機制有一定了解,這里不詳細分析**系統的資源加載機制**,只是簡單說明一下。**拿最簡單的圖片來說,當我們把一張圖片放在drawable目錄后,就可以通過Resources去獲取這張圖片。同時為了兼容不同的設備,我們可能還需要在其他一些目錄放置不同的圖片,比如drawable-mdpi、drawable-hdpi、drawable-land等。這樣,當應用程序啟動時,系統就會根據當前設備的情況去加載合適的Resources資源,比如說橫屏手機和豎屏手機會拿到兩張不同的圖片(設定了landscape或者portrait狀態下的圖片)。比如說當前Activity處于豎屏狀態,如果突然旋轉屏幕,由于系統配置發生了改變,在默認情況下,Activity就會被銷毀并且重新創建**,當然我們也可以阻止系統重新創建我們的Activity。 **在默認情況下,如果我們的Activity不做特殊處理,那么當系統配置發生改變后,Activity就會被銷毀并重新創建**,其生命周期如圖1-3所示。 :-: ![](https://img.kancloud.cn/45/40/4540116ed4f76b0bfa3ba6bc9cf646ff_1438x757.png) 圖1-3 異常情況下Activity的重建過程 當系統配置發生改變后,Activity會被銷毀,其onPause、onStop、onDestroy均會被調用,同時**由于Activity是在異常情況下終止的,系統會調用onSaveInstanceState來保存當前Activity的狀態。這個方法的調用時機是在onStop之前,它和onPause沒有既定的時序關系,它既可能在onPause之前調用,也可能在onPause之后調用。需要強調的一點是,這個方法只會出現在Activity被異常終止的情況下,正常情況下系統不會回調這個方法。當Activity被重新創建后,系統會調用onRestoreInstanceState,并且把Activity銷毀時onSaveInstanceState方法所保存的Bundle對象作為參數同時傳遞給onRestoreInstanceState和onCreate方法。因此,我們可以通過onRestoreInstanceState和onCreate方法來判斷Activity是否被重建了,如果被重建了,那么我們就可以取出之前保存的數據并恢復,從時序上來說,onRestoreInstanceState的調用時機在onStart之后**。 同時,我們要知道,**在onSaveInstanceState和onRestoreInstanceState方法中,系統自動為我們做了一定的恢復工作。當Activity在異常情況下需要重新創建時,系統會默認為我們保存當前Activity的視圖結構,并且在Activity重啟后為我們恢復這些數據**,比如文本框中用戶輸入的數據、ListView滾動的位置等,這些View相關的狀態系統都能夠默認為我們恢復。具體針對某一個特定的View系統能為我們恢復哪些數據,我們可以查看View的源碼。**和Activity一樣,每個View都有onSaveInstanceState和onRestoreInstanceState這兩個方法,看一下它們的具體實現,就能知道系統能夠自動為每個View恢復哪些數據**。 關于**保存和恢復View層次結構,系統的工作流程**是這樣的:**首先Activity被意外終止時,Activity會調用onSaveInstanceState去保存數據,然后Activity會委托Window去保存數據,接著Window再委托它上面的頂級容器去保存數據。頂層容器是一個ViewGroup,一般來說它很可能是DecorView。最后頂層容器再去一一通知它的子元素來保存數據,這樣整個數據保存過程就完成了**。可以發現,這是一種**典型的委托思想,上層委托下層、父容器委托子元素去處理一件事情**,這種思想在**Android中有很多應用**,比如**View的繪制過程、事件分發等都是采用類似的思想**。至于數據恢復過程也是類似的,這里就不再重復介紹了。接下來舉個例子,拿TextView來說,我們分析一下它到底保存了哪些數據。 源碼:TextView# onSaveInstanceState ``` @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); // Save state if we are forced to boolean save = mFreezesText; int start = 0; int end = 0; if (mText ! = null) { start = getSelectionStart(); end = getSelectionEnd(); if (start >= 0 || end >= 0) { // Or save state if there is a selection save = true; } } if (save) { SavedState ss = new SavedState(superState); // XXX Should also save the current scroll position! ss.selStart = start; ss.selEnd = end; if (mText instanceof Spanned) { Spannable sp = new SpannableStringBuilder(mText); if (mEditor ! = null) { removeMisspelledSpans(sp); sp.removeSpan(mEditor.mSuggestionRangeSpan); } ss.text = sp; } else { ss.text = mText.toString(); } if (isFocused() && start >= 0 && end >= 0) { ss.frozenWithFocus = true; } ss.error = getError(); return ss; } return superState; } ``` 從上述源碼可以很容易看出,TextView保存了自己的文本選中狀態和文本內容,并且通過查看其onRestoreInstanceState方法的源碼,可以發現它的確恢復了這些數據,具體源碼就不再貼出了,讀者可以去看看源碼。下面我們看一個實際的例子A,**來對比一下Activity正常終止和異常終止的不同,同時驗證系統的數據恢復能力。為了方便,我們選擇旋轉屏幕來異常終止Activity**,如圖1-4所示。 **示例A** :-: ![](https://img.kancloud.cn/ab/b3/abb3db2821c808c7edcf3fad524c7e22_1436x508.png) 圖1-4 Activity旋轉屏幕后數據的保存和恢復 通過**圖1-4可以看出,在我們選擇屏幕以后,Activity被銷毀后重新創建,我們輸入的文本“這是測試文本”被正確地還原,這說明系統的確能夠自動地做一些View層次結構方面的數據存儲和恢復**。下面再用一個例子B,來驗證我們自己做數據存儲和恢復的情況,代碼如下: **示例B** @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState ! = null) { String test = savedInstanceState.getString("extra_test"); Log.d(TAG, "[onCreate]restore extra_test:" + test); } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Log.d(TAG, "onSaveInstanceState"); outState.putString("extra_test", "test"); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); String test = savedInstanceState.getString("extra_test"); Log.d(TAG, "[onRestoreInstanceState]restore extra_test:" + test); } 上面的代碼很簡單,首先我們**在onSaveInstanceState中存儲一個字符串,然后當Activity被銷毀并重新創建后,我們再去獲取之前存儲的字符串。接收的位置可以選擇onRestoreInstanceState或者onCreate**,二者的區別是:**onRestoreInstanceState一旦被調用,其參數Bundle savedInstanceState一定是有值的,我們不用額外地判斷是否為空;但是onCreate不行,onCreate如果是正常啟動的話,其參數Bundle savedInstanceState為null,所以必須要額外判斷。這兩個方法我們選擇任意一個都可以進行數據恢復,但是官方文檔的建議是采用onRestoreInstanceState去恢復數據**。下面我們看一下運行的日志,如圖1-5所示。 :-: ![](https://img.kancloud.cn/5f/87/5f87fd555a2c8d9d6b8a92a8a5e77396_1439x336.png) 圖1-5 系統日志 如圖1-5所示,**Activity被銷毀了以后調用了onSaveInstanceState來保存數據,重新創建以后在onCreate和onRestoreInstanceState中都能夠正確地恢復我們之前存儲的字符串**。這個例子很好地證明了上面我們的分析結論。**針對onSaveInstanceState方法**還有一點需要說明,那就是**系統只會在Activity即將被銷毀并且有機會重新顯示的情況下才會去調用它**。考慮這么一種情況,**當Activity正常銷毀的時候,系統不會調用onSaveInstanceState,因為被銷毀的Activity不可能再次被顯示**。這句話不好理解,但是我們可以對比一下**旋轉屏幕所造成的Activity異常銷毀,這個過程和正常停止Activity是不一樣的,因為旋轉屏幕后,Activity被銷毀的同時會立刻創建新的Activity實例,這個時候Activity有機會再次立刻展示,所以系統要進行數據存儲。這里可以簡單地這么理解,系統只在Activity異常終止的時候才會調用onSaveInstanceState和onRestoreInstanceState來存儲和恢復數據,其他情況不會觸發這個過程**。 >[info]注意:但是按Home鍵后者啟動新的Activity仍然會單獨觸發onSaveInstanceState的調用,當然,這種情況和上面所說的異常終止Activity也不一樣。 **2.情況2:資源內存不足導致低優先級的Activity被殺死** 這種情況我們不好模擬,但是其數據存儲和恢復過程和情況1完全一致。這里我們描述一下Activity的優先級情況Activity按照優先級從高到低,可以分為如下三種: * (1)**前臺Activity**——正在和用戶交互的Activity,**優先級最高**。 * (2)**可見但非前臺Activity**——比如Activity中彈出了一個對話框,導致Activity可見但是位于后臺無法和用戶直接交互。 * (3)**后臺Activity**——已經被暫停的Activity,比如執行了onStop,**優先級最低**。 **當系統內存不足時,系統就會按照上述優先級去殺死目標Activity所在的進程,并在后續通過onSaveInstanceState和onRestoreInstanceState來存儲和恢復數數據。如果一個進程中沒有四大組件在執行,那么這個進程將很快被系統殺死,因此,一些后臺工作不適合脫離四大組件而獨自運行在后臺中,這樣進程很容易被殺死。比較好的方法是將后臺工作放入Service中從而保證進程有一定的優先級,這樣就不會輕易地被系統殺死**。 上面分析了系統的數據存儲和恢復機制,我們知道,**當系統配置發生改變后,Activity會被重新創建,那么有沒有辦法不重新創建呢?答案是有的**,接下來我們就來分析這個問題。**系統配置中有很多內容,如果當某項內容發生改變后,我們不想系統重新創建Activity,可以給Activity指定configChanges屬性。比如不想讓Activity在屏幕旋轉的時候重新創建,就可以給configChanges屬性添加orientation這個值**,如下所示。 ``` android:configChanges="orientation" ``` **如果我們想指定多個值,可以用“|”連接起來**,比如android:configChanges="orientation|keyboardHidden"。系統配置中所含的項目是非常多的,下面介紹每個項目的含義,如表1-1所示。 :-: 表1-1 configChanges的項目和含義 ![](https://img.kancloud.cn/93/c0/93c05be329eb581e8bd1778b996ad507_1012x742.png) 從表1-1可以知道,**如果我們沒有在Activity的configChanges屬性中指定該選項的話,當配置發生改變后就會導致Activity重新創建。上面表格中的項目很多,但是我們常用的只有locale、orientation和keyboardHidden這三個選項,其他很少使用**。 >[info]需要注意的是screenSize和smallestScreenSize,它們兩個比較特殊,它們的行為和編譯選項有關,但和運行環境無關。 下面我們再看一個demo,看看當我們指定了configChanges屬性后,Activity是否真的不會重新創建了。我們所要修改的代碼很簡單,只需要在AndroidMenifest.xml中加入Activity的聲明即可,代碼如下: <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="19" /> <activity android:name="com.ryg.chapter_1.MainActivity" android:configChanges="orientation|screenSize" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); Log.d(TAG, "onConfigurationChanged, newOrientation:" + newConfig. orientation); } 需要說明的是,由于編譯時筆者指定的minSdkVersion和targetSdkVersion有一個大于13,所以為了防止旋轉屏幕時Activity重啟,除了orientation,我們還要加上screenSize,原因在上面的表格里已經說明了。其他代碼還是不變,運行后看看log,如圖1-6所示。 :-: ![](https://img.kancloud.cn/d6/da/d6dac9e19234330de6b9b246121fa3f1_1435x211.png) 圖1-6 系統日志 由上面的日志可見,**Activity的確沒有重新創建,并且也沒有調用onSaveInstanceState和onRestoreInstanceState來存儲和恢復數據,取而代之的是系統調用了Activity的onConfigurationChanged方法,這個時候我們就可以做一些自己的特殊處理了**。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看