谈一谈在Activity 中释放资源
最近的项目中,出现了一个很有意思的bug, 具体的细节如下:
在 Activity A 中,跳转到 Activity B,存在一个单例对象,在Activity 的 onCreate()进行初始化,然后在 onDestroy()里面进行重置,具体的调用代码如下:
Activity A:
public class ViewStubLayoutParamsActivity extends Activity{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
.....
initView();
SingleTone.getInstance().setRoomId(10);
}
@Override
protected void onDestroy() {
super.onDestroy();
SingleTone.getInstance().clear();
}
private void initView(){
findViewById(R.id.tv_viewstub).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(ViewStubLayoutParamsActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
});
}
}
代码很简单,就是点击一个按钮,然后跳转另一个 Activity。另一个 Activity 的代码如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SingleTone.getInstance().setRoomId(11);
}
@Override
protected void onDestroy() {
super.onDestroy();
SingleTone.getInstance().clear();
}
}
但是实际上的结果是,我在 Activity B 里面拿到的值,是被重置后的值,也就是 clear()方法重置后的值;这里的原因是Activity 生命周期的问题,这个场景中,Activity 的生命周期调用方法如下:
所以,在 Activity B onCreate()执行之后, Activity A 的 onDestroy()才执行,导致单例对象被重置。
那么我们有没有办法避免这个问题?答案是可以的。我们可以在 finish()方法里面去释放资源,
public void finish() {
finish(DONT_FINISH_TASK_WITH_ACTIVITY);
}
Activity A 的 finish()方法,会在 onDestroy() 之前调用,并且会在 Activity B 的onCreate() 之前调用,这样就达到了重置数据的目的。
释放资源
考虑以下几种场景:
- Activity 是栈顶 Activity,由于回退被销毁,则所有资源需要释放,可以在 OnDestroy()里面释放。
- Activity 由于切换被压入 Activity 栈,失去栈顶位置,并且没有调用 finish()方法。由于 Activity 只会经历 onPause()和onStop()这两个方法都不适合做大量的操作,否则会阻塞新的 Activity 启动。
- 同样是场景二,但是调用了 finish()方法,这时,如果是单例类的资源,可以在 finish()方法释放,如果是View 相关资源,数据库资源等最好在 OnDstory()方法里面释放。
原理解析
刚刚说的施放资源的时机,需要你掌握Activty 启动相关的知识。这里做一些简单的分析(基于 API 25 ):Activity A 启动 Activty B 的时候,调用 startActivity()方法,其执行步骤大致如下:
Activity A 发送进程间消息到 ActivityManagerService,告诉AMS 要启动 Activity B
AMS 会将需要启动的 Activity B 的信息保存下来,然后发送信息告知 Activity A 进行中止操作。
Activity A 收到中止信息,进行中止操作,并且通知 AMS 自己已经中止了。
AMS 将保存下来的 Activity B 的信息,发送给 Activity A 和 B(假设它们是同一个进程)所在的进程,从而在对应的进程执行 Activity B 的启动操作。
后序如果 Activity A 调用了 finish()方法,其 onDestroy()方法才会被调用。