Android應用資源—處理運行時改變(Handing Runtime Changes)

某些設備配置能夠在運行期間進行改變(如屏幕的方向、鍵盤可用性、語言等)。當這樣的改變發生時,Android會重啟正在運行的Activity(onDestroy()回調之後,緊跟著調用onCreate()回調方法)。設計這種重啟行為有助於應用程序通過重啟,重新載入跟新設備配置相匹配的可選資源。

要正確的處理重啟,重要的是要恢復Activity之前的生存周期狀態,因此在Activity被銷毀之前,Android會調用onSaveInstanceState()回調方法來保存應用程序相關的狀態數據。這樣在Activity的onCreate()或onRestoreInstanceState()被調用期間就可以恢復之前的狀態瞭。

要測試應用程序重啟後的狀態的完整性,就應該在程序運行時改變設備配置(如改變屏幕方向)。應用程序應該在處理配置改變、電話接入等事件的任何時候,都能夠重啟而且不丟失用戶數據和狀態。要瞭解Activity狀態是如何被恢復的,請閱讀Activity的生存周期。

但是,可能會遇到在重啟應用程序時要恢復大量數據的情況,這時可能會給用戶帶來不好的用戶體驗。這樣情況,有兩個選擇:

1.  在配置改變期間保留一個對象。

當配置改變時,允許Activity重啟,但是要把狀態對象攜帶給Activity新的實例。

2. 自行處理配置的變化

在某個配置改變期間,防止系統重啟Activity,但是要在配置改變時接收一個回調方法,以便在必要時手動的更新Activity。

 

在配置變化期間保留一個對象

如果在重啟Activity時,要求恢復大數據集、重建網絡連接、或執行其他的敏感操作,那麼由於配置的改變完全重啟會降低用戶體驗。此外,系統也不可能用onSaveInstanceState()回調方法的Bundle對象來完整的保存要恢復的數據,因為Bundle對象沒有被設計用來攜帶大對象(如位圖)和大數據,這樣導致Bundle對象在系列化和隨後的反系列化時要占用大量的內存,從而導致配置改變緩慢。這種情況下,可以選擇保留一個狀態對象來減輕恢復Activity重啟時的負擔。

保留運行期間配置改變對象的方法如下:

1.  重寫onRetainNonConfigurationInstance()回調方法,它會返回希望保留的對象。

2.  在Activity被重建時,調用getLastNonConfigurationInstance()方法來恢復這個對象。

當Android系統由於配置的變化關掉Activity時,它會在onStop()回調和onDestroy()之間調用onRetainNonConfigurationInstance()回調方法。在onRetainNonConfigurationInstance()回調的實現中,為瞭在配置變化之後能夠有效的恢復狀態,它可以返回任意任何需要的對象。

如果應用程序要從網絡上加載數據,這樣做就很有價值。如果用戶改變瞭設備的方向,並且Activity重啟瞭,應用程序就必須重新獲取數據,這樣會很慢。因此可以實現onRetainNonConfigurationInstance()方法,讓它返回一個帶有數據的對象,然後再Activity被重啟時,再用getLastNonConfigurationInstance()方法來獲取這個被保留的對象,例如:

@Override
public Object onRetainNonConfigurationInstance() {
    final MyDataObject data = collectMyLoadedData();
    return data;
}

警告:在返回對象時,不要帶有跟Activity綁定的對象,如Drawable、Adapter、View或其他跟Context關聯的對象。如果這樣做瞭,會導致最初的Activity實例的所有View對象和資源泄漏(泄漏資源意味著應用程序占用瞭這些資源,因而不能被作為垃圾來回收,這樣就會導致大量的內存占用)。

Activity重啟時,恢復數據的方法:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    final MyDataObject data = (MyDataObject) getLastNonConfigurationInstance();
    if (data == null) {
        data = loadMyData();
    }
    …
}

這個例子中,getLastNonConfigurationInstance()方法返回被onRetainNonConfigurationInstance()方法保存的數據。如果data對象是null(在由於配置改變以外的其他原因而導致的Activity重啟時,會發生這種情況),那麼代碼就會從初始資源中裝載數據對象。

自行處理配置變化 www.aiwalls.com

如果在特定的配置變化期間,應用程序不需要更新資源,並且由於性能的限制,要求避免Activity的重啟,那麼可以聲明讓Activity自行處理配置的變化,從而防止系統重啟Activity。

註意:自行處理配置的變化使得使用可選資源更加困難,因為系統不能自動的應用它們。這種技術應該在必須避免Activity重啟(由於配置的變化)時,最後才考慮使用,並且大數應用程序不推薦使用。

要讓Activity處理配置的變化,就要編輯清單文件中相應的<activity>元素,讓它包含android:configChanges屬性,這個屬性值代表瞭要處理的配置。可能的值在android:configChanges屬性文檔中被列出(最常使用的值時“orientation”和“keyboardHidden”,orientation用於在屏幕方向變化時,防止Activity的重啟;keyboardHidden用於在鍵盤的可用性發生變化時,防止Activity重啟)。通過用管道符“|”分離,可以給這個屬性聲明多個配置值。

例如,下列清單代碼就聲明瞭一個自行處理屏幕方向變化和鍵盤可用性變化的Activity:

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

通過上述聲明,當這些配置中其中之一發生變化時,MyActivity就不會重啟瞭,相反,MyActivity會接收onConfigurationChanged()方法的回調。這個方法被傳入瞭一個Configuration對象,它指定瞭新的設備配置。通過讀取Configuration對象中的字段,就能夠判斷新的配置,並通過更新界面中使用的資源來進行對應的改變。在調用這個方法時,Activity的Resources對象會被基於新配置所返回的資源更新,因此不用系統重啟Activity,就能夠容易的重設UI元素。

警告:從Android3.2(API級別13)開始,設備在縱向和橫向之間切換時,“屏幕尺寸”也會發生改變。因此在使用API級別13或更高版本開發時,如果想要在運行時防止由於方向的變化而導致的重啟,就必須在android:configChanges的屬性值中包含“screenSize”。也就是說,必須聲明android:configChanges=”orientation|screenSize”。但是,如果應用的目標平臺是API級別12或更低版本,那麼Activity就會始終自行處理配置的變化(這樣配置的變化就不會重啟Activity,即使是在Android3.2或更高版本的設備上運行,也不會重啟Activity)。

例如,下面的onConfigurationChanged()方法實現瞭對當前設備方向的檢查:

@Override
publicvoid 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();
    }elseif(newConfig.orientation ==Configuration.ORIENTATION_PORTRAIT){
        Toast.makeText(this,"portrait",Toast.LENGTH_SHORT).show();
    }
}

Configuration對象代表瞭所有的當前配置,不僅僅針對改變的那個配置。大多數時候,不必關註配置是如何變化的,隻需把提供的可選資源重新分配給正在處理的配置就可以瞭。例如,因為Resources對象的更新,就要用setImageResource()方法重設所有ImageView對象,並且給新的配置使用適當的資源。

我們註意到,來自Configuration字段的值是整數,它要跟Configuration類中指定的常量進行匹配。關於這些常量的說明,請參考Configuration類說明。

記住:當聲明瞭要Activity自行處理配置變化時,就要手動的負責重設Activity中的任何UI元素。如果聲明瞭Activity要處理方向的變化,並且在橫向和縱向之間切換時有圖標的變化,那麼在onConfigurationChanged()執行期間,就應該重新分配每個資源。

如果不需要基於這些配置變化來更新應用程序,就不必實現onConfigurationChanged()方法。這樣,所有的在配置變化之前使用的資源會在變化之後依然使用,僅僅是避免瞭Activity的重啟。但是,應用程序使用能夠關閉並用之前完整的狀態來重啟,因此,在普通Activity的生存期間不應該考慮使用這種技術,這不僅是因為這樣不能阻止其他配置變化所導致的應用程序重啟,而且也因為應該處理諸如用戶離開應用程序、用戶返回應用之前獲取被銷毀的狀態等事件。

關於在Activity中能夠處理的配置變化的更多信息,請看android:configChanges文檔和Configuration類。

摘自  FireOfStar的專欄

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。