一、背景
我们在使用android app的过程中,可能突然会遇到其他的事情需要将app退到后台,比如微信来了一条消息需要回复,当我们回复完消息在切换回我们的app时,如果处理不好,应用可能会白屏或者闪退。造成这种现象的原因,是android的进程回收机制,android会给每一个应用分配一定的内存,当应用退入后台时,系统并不会将应用杀死,而是将应用缓存起来。打开的应用越多,后台缓存的进程也越多,那么占用的内存也越大,当系统内存不足时,就会根据自己的一套进程回收机制去杀死应用,以便腾出内存空间来分配给其他的应用,后面会详细分析android的进程回收机制。也就是说,当我们将应用退入后台后,应用很可能会因为系统内存不足而被杀死,应用被杀死后,所有的Activity都被销毁了,但是Android也提供了一定的补救措施,activity棧并没有被清空,也就是说如果之前的Activity棧中的内容是A-->B-->C,只是ABC这三个Activity的对象被销毁了,当我们切换应用时,系统首先会重新创建Activity C,由于需要重新创建Activity实例,需要耗费一点时间,所有会有短暂的白屏现象。另外当Activity被意外杀死时,Android会调用Activity的onSaveInstance让开发者去保存应用的数据,在重新创建Activity时,会调用onRestoreInstanceState来恢复应用数据,但是针对大量需要恢复的数据,一旦处理不当,就有可能造成应用闪退,比如,应用在后台被杀死后,所有的静态变量都会被清空,这个时候我们如果直接使用这些静态变量,就会报空指针异常,从而造成程序闪退。
二、解决办法
针对需要恢复的数据比较少的应用,我们可以通过Activity的onSaveInstance和onRestoreInstanceState去恢复,针对静态变量比较多的情况,我们可以考虑采用数据持久化的方式,所有的静态对象都改为单例模式,然后附加上一些持久化cache,空了再取缓存。嗯,这肯定也是一个办法,但是这样的束手束脚对开发来说也是痛苦,一旦处理漏了就会发生空指针异常。我们能不能换一种思路来解决上面的问题,当应用在后台被杀死时,不做Activity棧的恢复,清空Activity棧,并且直接启动应用的第一个Activity,一般是欢迎界面,这样的话,应用就相当于重启了一次,所有的逻辑都会重新开始,也就不存在静态变量为空的问题了,目前很多应用都是这样处理的。那么我们如何判断应用已经被杀死并重新创建了呢?因为静态变量的生命周期和应用的生命周期是一致的,应用被杀死后,静态变量会被回收掉,重新启动静态变量的数据会重新初始化,可以考虑定义在应用中定义一个静态变量,初始值为0,在应用的第一个界面的onCreate方法中将静态变量设置为1,表示应用走的是正常的启动流程,然后写一个基类的BaseActivity,其他的Activity都继承BaseActivity,在BaseActivity的onCreate方法中判断静态变量的值是否为1,如果不为1,则表示应用不是通过正常的流程启动的,则清空Activity棧,并启动第一个Activity。具体代码如下:
package com.liunian.androidbasic;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
/**
* Created by dell on 2018/4/13.
*/
public class App extends Application {
public final static int APP_STATUS_KILLED = 0; // 表示应用是被杀死后在启动的
public final static int APP_STATUS_NORMAL = 1; // 表示应用时正常的启动流程
public static int APP_STATUS = APP_STATUS_KILLED; // 记录App的启动状态
private static Context context;
@Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
public static Context getAppContext() {
return context;
}
/**
* 重新初始化应用界面,清空当前Activity棧,并启动欢迎页面
*/
public static void reInitApp() {
Intent intent = new Intent(getAppContext(), SplashActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
getAppContext().startActivity(intent);
}
}
package com.liunian.androidbasic;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
/**
* 启动欢迎页面
*/
public class SplashActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
App.APP_STATUS = App.APP_STATUS_NORMAL; // App正常的启动,设置App的启动状态为正常启动
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
goMain();
}
/**
* 去主页面
*/
private void goMain() {
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
}
package com.liunian.androidbasic.base;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import com.liunian.androidbasic.App;
/**
* Created by dell on 2018/4/17.
* 用来处理应用在后台被杀死后,让应用重新走启动流程
*/
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (App.APP_STATUS != App.APP_STATUS_NORMAL) { // 非正常启动流程,直接重新初始化应用界面
Log.i("liunianprint:", "reInitApp");
App.reInitApp();
finish();
return;
} else { // 正常启动流程
setUpViewAndData(savedInstanceState); // 子Activity初始化界面
}
}
/**
* 提供给子Activity设置界面的接口,不要在onCreate中初始化界面
* @param savedInstanceState
*/
protected abstract void setUpViewAndData(@Nullable Bundle savedInstanceState);
}
这里需要注意一点,继承BaseActivity的Activity初始化界面不要在onCreate中处理,而应该在setUpViewAndData处理,因为在onCreate方法中静态变量可能为空,而只有应用时正常启动时,才会调用setUpViewAndData方法。
三、总结
1、Android系统会为每一个应用分配一定的内存,当系统内存不足时,Android系统会按照进程回收机制来杀死一部分应用,并回收内存提供给其他的应用;
2、应用被系统杀死后,应用中所有的数据都会被回收掉,但是Activity提供了一定的补救措施,应用中的Activity棧还是会保存下来,并且提供了onSaveInstance和onRestoreInstanceState来恢复界面数据,因为需要重新创建Activity对象,需要耗费一定时间,所以可能会有短暂的白屏现象;
3、面对大量要恢复的数据(特别是静态变量),如果我们采用恢复数据的方式来处理应用被系统杀死的问题,代码将会很复杂,我们必须非常小心的处理数据恢复的问题,并且数据恢复也存在一定的效率问题,毕竟需要将数据持久化;
4、针对应用被杀死的问题,可以采用重新初始化应用流程的方式来解决,也就是如果发现应用时非正常启动,则清空当前Activity棧,并且去启动应用的欢迎界面,这样就相当于重新打开应用一样,这种处理方式虽然没有应用被杀死前的数据,但是处理起来简单方便。
附上源码:https://github.com/2449983723/AndroidComponents