處理設定變更

Stay organized with collections Save and categorize content based on your preferences.

某些裝置設定可能會在執行階段期間變更 (例如螢幕方向、鍵盤可用性,以及使用者啟用多視窗模式)。有這種變更時,Android 會重新啟動執行的 Activity (系統會呼叫 onDestroy(),後面接著 onCreate())。重新啟動行為是為了讓應用程式根據與新裝置設定相符的額外資源自動重新載入應用程式,以協助應用程式適應新設定。

如要正確處理重新啟動,活動必須還原先前的狀態。您可以使用 onSaveInstanceState()ViewModel 物件組合及永久儲存空間,以儲存和還原設定變更的活動 UI 狀態。如要進一步瞭解如何儲存活動狀態,請參閱「儲存 UI 狀態」。

如要測試應用程式是否會自動重新啟動,且應用程式狀態完整,建議您在應用程式中執行不同的工作時呼叫設定變更 (例如變更螢幕方向)。您的應用程式應可在不會遺失使用者資料或狀態的情況下隨時重新啟動,以處理設定變更或使用者收到來電時等活動,然後在應用程式程序已經刪除後,等待一段時間後再返回應用程式。如要瞭解如何還原活動狀態,請參閱「活動生命週期」。

不過,在重新啟動應用程式後,如果還原了大量資料,也有可能導致成本過高,進而對使用者體驗造成負面影響。在這種情況下,您有兩種其他選擇:

  1. 在設定變更期間保留物件

    允許活動在設定變更時重新啟動,但將有狀態的物件導入活動的新執行個體。

  2. 自行處理設定變更

    由於處理設定變更可能會很複雜,因此不建議您自行處理設定變更。但是,如果無法使用偏好的選項 (onSaveInstanceState()ViewModel 物件和永久儲存空間) 保留 UI 狀態,您可以改為將系統設定為特定設定變更期間不要重新啟動活動。在設定變更時,應用程式會收到回呼,讓您可以視需要手動更新活動。

在設定變更期間保留物件

如果重新啟動活動需要您復原大量資料組合、重新建立網路連線或執行其他密集的作業,那麼因為設定變更可能會導致使用者體驗的速度便得緩慢,所以需要完全重新啟動。此外,您也許無法使用系統協助儲存的 Bundle,透過 onSaveInstanceState() 回呼完全還原活動狀態,因為這並非設計為可在其中存放大型物件 (例如:點陣圖) 和資料,而是必須先在主要執行緒中序列化,然後再取消序列化,而這可能會消耗大量記憶體,並導致設定變更速度變得緩慢。在這種情況下,您可以使用 ViewModel 緩解重新啟動部分活動時對系統造成的負擔。ViewModel 物件會在設定變更中保留,因此這是存放 UI 資料的最理想位置,也不需要再次查詢。如要進一步瞭解在應用程式中使用 ViewModel 類別,請參閱「ViewModel 總覽」。

自行處理設定變更

如果應用程式在特定設定變更期間不需要更新資源,且您有效能限制,從而必須避免活動重新啟動,那麼您就可以宣告活動自行處理設定變更,避免系統重新啟動您的活動。

注意:自行處理設定變更可能會增加使用額外資源的難度,而這是因為系統不會自動為您套用這些設定。建議您在因為設定變更而必須避免重新啟動時才考慮使用此技巧;對於大多數應用程式而言,不建議使用此技巧。

如要宣告活動處理設定變更,請在資訊清單檔案中編輯適當的 <activity> 元素,以加入 android:configChanges 屬性及代表要處理的設定值。可能的值會列在 android:configChanges 屬性的文件中。最常用的值是 "orientation""screenSize""screenLayout""keyboardHidden"

  • "orientation" 值可避免螢幕方向變更時重新啟動。
  • "screenSize" 值也會防止螢幕方向變更時重新啟動,但僅適用於 Android 3.2 (API 級別 13) 以上版本。
  • 系統必須使用 "screenLayout" 值,才能偵測裝置 (例如折疊式手機和有變形設計的 Chromebook) 可能會觸發的變更。
  • "keyboardHidden" 值可避免鍵盤使用情況變更時重新啟動。

如要手動處理應用程式中的螢幕方向變更,您必須在 android:configChanges 屬性中宣告 "orientation""screenSize""screenLayout" 值。如要在屬性中宣告多個設定值,請使用直立線 | 字元。

舉例來說,以下資訊清單程式碼會宣告同時處理螢幕方向變更和鍵盤可用性變更的活動:

<activity android:name=".MyActivity"
          android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
          android:label="@string/app_name">

現在,當其中一個設定發生變更時,MyActivity 就不會重新啟動,而是 MyActivity 會收到對 onConfigurationChanged() 的呼叫。這個方法會傳遞指定新裝置設定的 Configuration 物件。讀取 Configuration 物件中的欄位,您就可以決定新的設定,並以更新界面中使用的資源來做出合適的變更。呼叫此方法時,活動的 Resources 物件就會更新,以根據新設定傳回資源,因此您可以輕鬆地重設 UI 元素,而不需要系統重新啟動活動。

舉例來說,下列 onConfigurationChanged() 實作會檢查目前的裝置螢幕方向:

Kotlin

override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)

    // Checks the orientation of the screen
    if (newConfig.orientation === Configuration.ORIENTATION_LANDSCAPE) {
        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show()
    } else if (newConfig.orientation === Configuration.ORIENTATION_PORTRAIT) {
        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show()
    }
}

Java

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks the orientation of the screen
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
    }
}

Configuration 物件代表所有目前的設定,而不只是已變更的設定。在大多數情況下,您不用擔心設定做了哪些變更,只需要指派所有可提供額外資源至處理的設定即可。舉例來說,由於 Resources 物件現已更新,因此您可以使用 setImageResource() 及新設定使用的合適資源重設任何 ImageView 執行個體 (如應用程式資源總覽中所述)。

請注意,Configuration 欄位的值是與 Configuration 類別的特定常數相符的整數。如要瞭解每個欄位可搭配使用的常數,請參閱 Configuration 參考中的適當欄位。

注意:宣告活動以處理設定變更時,您必須負責重設提供額外資源的任何元素。如果宣告活動以處理螢幕方向變更,而且圖片應在橫向和直向之間切換,您就必須在 onConfigurationChanged() 期間重新指派每個資源。

如果不需要根據這些設定變更來更新應用程式,您可以改為「不」實作 onConfigurationChanged()。如果是這種情況,所有在設定變更前使用的資源都仍會繼續使用,只是不會重新啟動活動而已。

不過在正常的活動生命週期期間,請勿將此技巧視為保留狀態。應用程式應可隨時關閉和重新啟動,並且可完整保持其之前的狀態。無法停止的設定變更可能會重新啟動應用程式。如果使用者退出應用程式,且應用程式在背景中執行,系統可能會刪除該應用程式。