解决Android被系统回收,重打开崩溃等问题

来源:互联网 发布:linux系统当前版本 编辑:程序博客网 时间:2024/06/06 01:09

流程再现:
假设第一个进栈的是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的方法大致有如下几种:

  1. 创建static Set< Activity>,每个Activity创建时动态添加。但是刚才已经讨论过了,static会被回收,行不通;
  2. 广播注册法,Activity.onCreate中注册广播,一旦检测到被回收,发送广播全部关掉。被回收,其实就相当于Broadcast被unregister,不展开讨论;
  3. 通过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吧。


1 0
原创粉丝点击