【安卓篇】安卓Activity程序被殺規則
阿新 • • 發佈:2019-01-24
Android手機開發與桌面開發有一個主要不同之處:通常在一部Android手機裡同時執行著多個應用(app),每個app對應一個系統程序,當系統需要更多的資源(如記憶體)而空閒資源不足時,Android系統就會選擇殺掉一些“低優先順序”的程序以便釋放所需資源。
Android系統是如何確定程序優先順序的高低的呢?
如果一個app正在與使用者互動,那麼它所在的程序具有最高優先順序;其次,如果一個app是可見的,例如被一個對話方塊部分遮擋,它所在程序具有第二高的優先順序;再次,如果app當前是不可見的,也就是被切換到了後臺,則它所在程序具有第三高的優先順序;
這裡要補充一點,如果這個後臺app啟動了一個service,則它比一般的後臺app優先順序高一些。最後,如果一個程序裡沒有包含任何app,這個程序的優先順序是最低的。當系統資源嚴重不足時,任何一個程序都有可能被殺掉,而當用戶想回到一個已經不存在於記憶體中的Activity時,系統只得新建一個這樣的Activity物件並呼叫它的onCreate()方法進行恢復。
所以有時出現這種狀況:一個app大部分時間執行很好,偶爾在切換Activity時出現空指標異常導致強制關閉,這多半是在onCreate()方法裡使用了已經被重置為空的物件(例如intent裡的變數)造成的。即使不出現異常,也會造成表單資料丟失等嚴重影響使用體驗的問題。
要解決這類問題,切不可抱“現在手機記憶體大,程序一般不會被殺掉”這種僥倖心理,而應該以“應用隨時都會被殺掉”的態度來謹慎處理。
下面介紹Google建議的方式:
解決方法由於Activity隨時可能需要重建,所以我們要做的事情就是在適當的位置將Activity所需資料進行持久化(從ram複製到rom或sd卡),並在onCreate()方法裡利用這些資料恢復現場。
Activity有兩類資料需要進行持久化處理:“文件型別資料”和“內部狀態型別資料”,前者例如使用者正在編輯的表單,後者如使用者偏好。
一、為了持久化文件型別的資料,Google建議使用”即時生效”(edit in place)的編輯策略,具體的方式如下:使用者新建文件時,在SQLite資料庫(根據需要也可以使用preference)裡也立即新建一條記錄。(與此相對的方式是:為使用者提供一個“儲存”按鈕,只有當用戶按下按鈕時才將文件儲存到資料庫。)當用戶離開當前Activity時,onPause()方法會被觸發,在這個方法裡將當前正在編輯的文件持久化到資料庫。這樣一來,如果使用者是從這個Activity切換到另一個相關Activity,仍然可以看到剛剛儲存的內容。這種方式可以最大限度避免資料丟失,只要onPause()方法被觸發執行,即使Activity所在程序被系統kill掉也不會造成資料丟失。唯一要注意的是,介面上最好能提供一個“取消”按鈕或選單,以便讓使用者可以選擇不儲存對文件的更改。
二、為了持久化內部狀態型別的資料,可以在onPause()裡使用Activity#getPreferences(int)方法,這個方法返回SharedPreferences型別的物件,利用它可以記錄使用者對這個Activity的偏好資訊。例如一個日曆應用,使用者可以選擇顯示為周檢視或月檢視,這樣的資訊作為內部狀態記錄到SharedPreferences物件以後,下次再開啟這個Activity時就可以按使用者上次的選擇來顯示日曆了。以下程式碼來自官方文件,演示瞭如何持久化並恢復一個Activity的當前顯示模式:
publicclass CalendarActivity extends Activity {
staticfinalint DAY_VIEW_MODE = 0;
staticfinalint WEEK_VIEW_MODE = 1;
private SharedPreferences mPrefs;
privateint mCurViewMode;
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//取回之前持久化的日曆顯示模式
SharedPreferences mPrefs = getSharedPreferences();
mCurViewMode = mPrefs.getInt("view_mode", DAY_VIEW_MODE);
}
protectedvoid onPause() {
super.onPause();
//持久化日曆顯示模式
SharedPreferences.Editor ed = mPrefs.edit();
ed.putInt("view_mode", mCurViewMode);
ed.commit();
}
}
是有點麻煩,但為了能讓app穩定執行也值了。
有些同學要問了,為什麼是在onPause()裡持久化而不是在onSaveInstanceState()裡?
官方文件有下面一段話簡要解釋了原因,即前者比後者更可靠,因為onSaveInstanceState()不屬於Activity生命週期的一部分。
既然如此,我想不出onSaveInstanceState()還有什麼其他用途了,大家乾脆忘了它吧,還有onRestoreInstanceState()。
Note that it is important to save persistent data in onPause() instead of onSaveInstanceState(Bundle) because the latter is not part of the lifecycle callbacks, so will not be called in every situation as described in its documentation.
注1:關於上面這段話,網上存有一些爭議,我個人還是比較傾向在onPause()裡做持久化——既可靠又好記,唯一的缺點是呼叫次數稍多。
注2:從Android 3.0(HoneyComb)版本開始,Activity程序在被系統殺掉之前,將保證onStop()方法先執行完成,因此如果我們開發的應用只執行在3.0以上,可以把持久化工作放在onStop()裡以減少持久化的次數。最佳實踐不要抱僥倖心理,你的Activity隨時可能被銷燬。
解決方法:在onPause()裡持久化Activity資料,在onCreate()裡恢復現場。
參考資料:ActivitySaving Activity state in Android