流程再现:
假设第一个进栈的是FirstActivity,FirstActivity中打开SecondActivity,Second中用了First的静态变量list。如下:
public class FirstActivity extends BaseActivity {
public static List<String> list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.testxml);
list = new ArrayList<>();
startActivity(new Intent(this, SecondActivity.class));
}
}
public class SecondActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.testxml);
Log.e("~", FirstActivity.list.size() + "");
}
}
到SecondActivity界面时,按下Home键回到主界面,同时用AndroidStudio杀掉进程。
再点开应用,崩溃,报NullPointException,原因是SecondActivity重新onCreate中引用的list为空。
这只是一个简单的例子,app中有很多类似的问题可能会发生,导致应用Crash。
Android的垃圾回收机制会回收掉static关键字占用的内存,我们可以从这一点出发,来制定我们的解决方案。简单分为以下3个步骤:
Step 1:做一个单例,记录App状态;
public class AppStatusManager {
private static AppStatusManager mInstance = null;
private int appStatus = AppStatusConstant.APP_FORCE_KILLED;
private AppStatusManager() {
}
public static AppStatusManager getInstance() {
if(mInstance==null) {
synchronized (AppStatusManager.class) {
if(mInstance==null)
mInstance = new AppStatusManager();
}
}
return mInstance;
}
public void setAppStatus(int appStatus) {
this.appStatus = appStatus;
}
public int getAppStatus() {
return appStatus;
}
public static class AppStatusConstant {
/**
* App被回收,初始状态
*/
public static final int APP_FORCE_KILLED = 0;
/**
* 正常运行
*/
public static final int APP_NORMAL = 1;
}
}
Step 2:所有Activity的父类BaseActivity.onCreate中调用如下方法;
private void checkAppStatus() {
if(AppStatusManager.getInstance().getAppStatus()==AppStatusManager.AppStatusConstant.APP_FORCE_KILLED) {
//该应用已被回收,执行相关操作(下面有详解)
}
}
Step 3:应用栈底Activity.onCreate前把status改成运行状态;
@Override
protected void onCreate(Bundle savedInstanceState) {
AppStatusManager.getInstance().setAppStatus(AppStatusManager.AppStatusConstant.APP_NORMAL);
super.onCreate(savedInstanceState);
}
OK,到这里基本就已经大功告成了。
但是!!已经进栈的Activity还是存在的,如果用户点返回键回到上一个Activity,重新onCreate,还是有可能Crash。我们需要关闭之前所有的Activity,然后走重启流程。
关闭所有Activity的方法大致有如下几种:
- 创建static Set< Activity>,每个Activity创建时动态添加。但是刚才已经讨论过了,static会被回收,行不通;
- 广播注册法,Activity.onCreate中注册广播,一旦检测到被回收,发送广播全部关掉。被回收,其实就相当于Broadcast被unregister,不展开讨论;
- 通过startActivityForResult启动Activity,每个Activity.onActivityResult中进行不同的处理(finish() | 其它操作)。这么做确实可行,但是有2个问题。一,如果上个Activity.onCreate中有startActivity(),那么会再次启动;二,每个Activity都被串起来了,代码太多、耦合性太强,不建议;
看到这些都不可行,是不是感觉要GG了,大清要亡我啊!!!但正所谓“飞流直下三千尺,柳暗花明又一村。”,我们先来看Intent.java一个变量。
/**
* If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
* this flag will cause any existing task that would be associated with the
* activity to be cleared before the activity is started. That is, the activity
* becomes the new root of an otherwise empty task, and any old activities
* are finished. This can only be used in conjunction with {@link #FLAG_ACTIVITY_NEW_TASK}.
*/
public static final int FLAG_ACTIVITY_CLEAR_TASK = 0X00008000;
这一大堆英文是什么鬼!!!但是作为一名优秀的程序员,要有提炼精华的能力。上面一大堆英文,可以简括概要为一句:
the activity becomes the new root of an otherwise empty task, and old activities are finished.
大致意思就是,我们的应用开了个新的栈玩,之前那个栈里的Activity都会被关掉。至此,水落石出,借用这点完善上面的BaseActivity.checkAppStatus():
private void checkAppStatus() {
if(AppStatusManager.getInstance().getAppStatus()==AppStatusManager.AppStatusConstant.APP_FORCE_KILLED) {
//应用启动入口SplashActivity,走重启流程
Intent intent = new Intent(this, SplashActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
}
大功告成,赶紧回去解决你的BUG吧。