不管是安卓的官方文檔還是源碼注釋,處處可見“從 Activity A 跳到 Activity B,當系統(tǒng)內(nèi)存不足時 A 可能會被回收……”,而且沒有明確說明 A 和 B 是否屬于同一個 app 或進程。
但是,在官方給的 Activity 生命周期圖中,卻說內(nèi)存不足時低優(yōu)先級的進程將被殺死。

那么,內(nèi)存不足時,到底是 Activity 被回收了呢,還是進程被殺死了呢,還是二者都出現(xiàn)了呢?
答案是,Activity 被回收了,而且進程被殺死了,而且一般情況下該進程是后臺進程。當內(nèi)存不足時,系統(tǒng)會殺死優(yōu)先級低的后臺進程,進程內(nèi)的 Activity 肯定也就被回收了。
這就引申出另一個話題:app的進程被回收后,當用戶切回app時,我們應該怎樣保證activity的狀態(tài)得到恢復呢?
我們知道,在安卓開發(fā)中,當一個activity要啟動另一個activity時要傳遞數(shù)據(jù)的話,普遍的做法是將數(shù)據(jù)放在intent中:
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("data", "Data to Second Activity");
startActivity(intent);
在SecondActivity中可以通過intent.getStringExtra("data")獲得數(shù)據(jù)。如果這個數(shù)據(jù)要往更深層的activity傳遞的話,就要繼續(xù)將其放入啟動后續(xù)activity的intent中
String data = intent.getStringExtra("data");
// do something with data ....
Intent intent = new Intent(SecondActivity.this, ThirdActivity.class);
intent.putExtra("data", data);
startActivity(intent);
很多安卓開發(fā)的新手都會問,如果這個數(shù)據(jù)到在多個activity之間傳遞,為什么我們不把他作為成員變量放在一個全局的類(比如現(xiàn)成的Application類,或者一個全局可獲取的單例類)中,這樣這個數(shù)據(jù)就不用每次放在intent中傳來傳去了,豈不是方便很多?
答案很簡單:不能這樣做。如前面所述,當app處于后臺時被回收時,所有的全局單例類(包括Application實例)都會被銷毀,存在里面的數(shù)據(jù)也就跟著丟失了。當app被切回前臺時,依賴于這些數(shù)據(jù)的activity就會取不到數(shù)據(jù)。
為什么用Intent傳數(shù)據(jù)沒有問題呢?因為系統(tǒng)維護的task和activity棧幫我們處理了Intent(以及其中的數(shù)據(jù))的保存和恢復。簡單來說,所有“曾用于啟動activity的intent”和“還沒有被銷毀的activity”都會被系統(tǒng)維護在task棧中,并且當進程被回收時,安卓系統(tǒng)會自動幫我們把這些信息保存起來。

當用戶嘗試把一個被回收的app切回前臺時,系統(tǒng)會用之前保存的task棧信息來嘗試把app恢復到被回收之前的狀態(tài),而通過intent傳遞的數(shù)據(jù)也在這個過程中得到了還原。下面用一個簡單的例子來說明。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.e("lifecycle", "MainActivity onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnStartSecond = (Button)findViewById(R.id.button_start_second);
btnStartSecond.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("lifecycle", "Start SecondActivity from MainActivity");
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("data", "Data to Second Activity");
startActivity(intent);
}
});
}
@Override
protected void onStart() {
Log.e("lifecycle", "MainActivity onStart");
super.onStart();
}
@Override
protected void onRestart() {
Log.e("lifecycle", "MainActivity onRestart");
super.onRestart();
}
@Override
protected void onResume() {
Log.e("lifecycle", "MainActivity onResume");
super.onResume();
}
@Override
protected void onPause() {
Log.e("lifecycle", "MainActivity onPause");
super.onPause();
}
@Override
protected void onStop() {
Log.e("lifecycle", "MainActivity onStop");
super.onStop();
}
}
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.e("lifecycle", "SecondActivity onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Intent intent = getIntent();
if(intent != null) {
String data = intent.getStringExtra("data");
Log.e("lifecycle", "data from getIntent(): " + data);
((TextView)findViewById(R.id.text_second)).setText(data);
} else {
Log.e("lifecycle", "getIntent() returns null");
}
}
@Override
protected void onStart() {
Log.e("lifecycle", "SecondActivity onStart");
super.onStart();
}
@Override
protected void onRestart() {
Log.e("lifecycle", "SecondActivity onRestart");
super.onRestart();
}
@Override
protected void onResume() {
Log.e("lifecycle", "SecondActivity onResume");
super.onResume();
}
@Override
protected void onPause() {
Log.e("lifecycle", "SecondActivity onPause");
super.onPause();
}
@Override
protected void onStop() {
Log.e("lifecycle", "SecondActivity onStop");
super.onStop();
}
}
我們的app lifecycletest的MainActivity啟動了SecondActivity并通過Intent傳遞了一個字符串數(shù)據(jù)。我們在SecondActivity界面把app切回后臺,然后通過啟動其他app占用內(nèi)存來促使系統(tǒng)回收lifecycletest app:

可以看到lifecycletest的進程已經(jīng)被回收了。當切回app時:

可以看到系統(tǒng)建立了一個新的進程,SecondActivity的生命周期函數(shù)被依次執(zhí)行,而onCreate函數(shù)中通過getIntent仍然能夠取到之前從MainActivity傳過來的數(shù)據(jù),用戶也正確地回到了SecondActivity界面,看到了正確的數(shù)據(jù)。
細心的讀者可以看到這時MainActivity的生命周期函數(shù)沒有被執(zhí)行。這是因為系統(tǒng)恢復進程被回收的app時,只會執(zhí)行task棧頂?shù)腶ctivity的生命周期函數(shù)。如果用戶點返回的話,會執(zhí)行MainActivity的生命周期函數(shù),正確的退回到MainActivity。

需要注意的是,一個intent里面能放的數(shù)據(jù)大小是有限制的,最好是不超過500kb(不同的系統(tǒng)版本限制不一,可參考相關(guān)資料)。比如在intent里面放一個byte[ ]數(shù)組的話,如果數(shù)組大小太大,就會導致運行時拋異常。所以如果要在activity之間傳大量數(shù)據(jù)的話,最好把數(shù)據(jù)存為臨時文件,然后在intent中傳文件的路徑。
總結(jié):
1.當app處于后臺被系統(tǒng)回收時,app的進程被殺死了,Activity 也被回收了,而app的task和activity棧以及相應的intent和數(shù)據(jù)會被系統(tǒng)保存起來。當app被切回前臺時,系統(tǒng)會恢復task和activity棧以及相應的intent和數(shù)據(jù)。
2.不要在Application類和全局單例類中存放數(shù)據(jù),會導致app無法正確恢復狀態(tài)。運行時的臨時數(shù)據(jù)應存放在SharedPreference、臨時文件或數(shù)據(jù)庫中
3 Activity之間傳數(shù)據(jù)應該用系統(tǒng)提供的intent機制。