不管是安卓的官方文档还是源码注释,处处可见“从 Activity A 跳到 Activity B,当系统内存不足时 A 可能会被回收……”,而且没有明确说明 A 和 B 是否属于同一个 app 或进程。
但是,在官方给的 Activity 生命周期图中,却说内存不足时低优先级的进程将被杀死。
image.png
那么,内存不足时,到底是 Activity 被回收了呢,还是进程被杀死了呢,还是二者都出现了呢?
答案是,Activity 被回收了,而且进程被杀死了,而且一般情况下该进程是后台进程。当内存不足时,系统会杀死优先级低的后台进程,进程内的 Activity 肯定也就被回收了。
这就引申出另一个话题:app的进程被回收后,当用户切回app时,我们应该怎样保证activity的状态得到恢复呢?
我们知道,在安卓开发中,当一个activity要启动另一个activity时要传递数据的话,普遍的做法是将数据放在intent中:
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("data", "Data to Second Activity");
startActivity(intent);
在SecondActivity中可以通过intent.getStringExtra("data")获得数据。如果这个数据要往更深层的activity传递的话,就要继续将其放入启动后续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);
很多安卓开发的新手都会问,如果这个数据到在多个activity之间传递,为什么我们不把他作为成员变量放在一个全局的类(比如现成的Application类,或者一个全局可获取的单例类)中,这样这个数据就不用每次放在intent中传来传去了,岂不是方便很多?
答案很简单:不能这样做。如前面所述,当app处于后台时被回收时,所有的全局单例类(包括Application实例)都会被销毁,存在里面的数据也就跟着丢失了。当app被切回前台时,依赖于这些数据的activity就会取不到数据。
为什么用Intent传数据没有问题呢?因为系统维护的task和activity栈帮我们处理了Intent(以及其中的数据)的保存和恢复。简单来说,所有“曾用于启动activity的intent”和“还没有被销毁的activity”都会被系统维护在task栈中,并且当进程被回收时,安卓系统会自动帮我们把这些信息保存起来。
image.png当用户尝试把一个被回收的app切回前台时,系统会用之前保存的task栈信息来尝试把app恢复到被回收之前的状态,而通过intent传递的数据也在这个过程中得到了还原。下面用一个简单的例子来说明。
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传递了一个字符串数据。我们在SecondActivity界面把app切回后台,然后通过启动其他app占用内存来促使系统回收lifecycletest app:
image.png
可以看到lifecycletest的进程已经被回收了。当切回app时:
image.png
可以看到系统建立了一个新的进程,SecondActivity的生命周期函数被依次执行,而onCreate函数中通过getIntent仍然能够取到之前从MainActivity传过来的数据,用户也正确地回到了SecondActivity界面,看到了正确的数据。
细心的读者可以看到这时MainActivity的生命周期函数没有被执行。这是因为系统恢复进程被回收的app时,只会执行task栈顶的activity的生命周期函数。如果用户点返回的话,会执行MainActivity的生命周期函数,正确的退回到MainActivity。
image.png
需要注意的是,一个intent里面能放的数据大小是有限制的,最好是不超过500kb(不同的系统版本限制不一,可参考相关资料)。比如在intent里面放一个byte[ ]数组的话,如果数组大小太大,就会导致运行时抛异常。所以如果要在activity之间传大量数据的话,最好把数据存为临时文件,然后在intent中传文件的路径。
总结:
1.当app处于后台被系统回收时,app的进程被杀死了,Activity 也被回收了,而app的task和activity栈以及相应的intent和数据会被系统保存起来。当app被切回前台时,系统会恢复task和activity栈以及相应的intent和数据。
2.不要在Application类和全局单例类中存放数据,会导致app无法正确恢复状态。运行时的临时数据应存放在SharedPreference、临时文件或数据库中
3 Activity之间传数据应该用系统提供的intent机制。
网友评论