问题:
app运行在后台,android系统会在内存不够用的时候,回收app,如果app中有全局的变量,那么再次打开app可能会出现崩溃的情况。
示例:
public class MyApplication extends Application {
String name;
String getName() {
return name;
}
void setName(String name) {
this.name = name;
}
public static MyApplication sMyApplication;
public static MyApplication getApplication(){
if (sMyApplication == null) {
synchronized (MyApplication.class) {
if (sMyApplication == null) {
return sMyApplication = new MyApplication();
}
}
}
return sMyApplication;
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyApplication app = (MyApplication) getApplication();
app.setName("Developer Phil");
Toast.makeText(this, "1:" + app.getName(),Toast.LENGTH_LONG).show();
Log.d("1:",app.getName());
startActivity(new Intent(this, Main2Activity.class));
}
}
public class Main2Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Toast.makeText(this, "2:onCreate()",Toast.LENGTH_LONG).show();
Log.d("2:onCreate()","onCreate()");
}
@Override
protected void onResume() {
super.onResume();
MyApplication app = (MyApplication) getApplication();
Toast.makeText(this, "2:"+app.getName().toUpperCase(),Toast.LENGTH_LONG).show();
Log.d("2:",app.getName());
}
}
原因:
程序之所以会崩溃掉是因为恢复之后APP的Application对象是全新的,所以缓存在Application中的用户名成员变量为空值,在程序调用String的toUpperCase()方法时由于NullPointerException而崩溃掉。
导致这个问题的主要原因是:Application对象并不是始终在内存中的,它有可能会由于系统内存不足而被杀掉(可通过下面的命令模拟)。但Android在你恢复这个应用时并不是重新开始启动这个应用,它会创建一个新的Application对象并且启动上次用户离开时的activity以造成这个app从来没有被kill掉得假象。
解决方法:
大概思路:
第一种:
A app的引导页面
B app的首页,启动模式为singleTask
app中设置一个静态变量,静态变量中的变量默认值设置为被系统回收,app正常启动后,设置为正常启动。
这样当app被回收后,再次打开app,静态变量中的变量为被系统回收,app从A界面重新启动,A启动B,由于activity栈中肯定存在B,并且B在栈底(因为B为app首页),所以启动B后,所有的activity都被清除。
实现了在保证栈顺序正确的情况下,实现了重新开启app
第二种
你也可以考虑,系统销毁activity后,再次开启app后,重新创建 Activity,使用函数 onSaveInstanceState()进行数据的保存
代码如下:
1、添加以下常量:
public static final int STATUS_FORCE_KILLED = -1;//应用在后台被强杀了
public static final int STATUS_NORMAL = 2; //APP正常态
public static final String START_LAUNCH_ACTION = "start_launch_action";
private int appStatus = Constants.STATUS_FORCE_KILLED; //默认为被后台回收了
private static AppUtils appStatusManager;
public static AppUtils getInstance() {
if (appStatusManager == null) {
appStatusManager = new AppUtils();
}
return appStatusManager;
}
public int getAppStatus() {
return appStatus;
}
public void setAppStatus(int appStatus) {
this.appStatus = appStatus;
}
2、BaseActivity ,每一个activity都继承BaseActivity
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
switch (AppUtils.getInstance().getAppStatus()) {
case Constants.STATUS_FORCE_KILLED:
restartApp();
break;
case Constants.STATUS_NORMAL:
break;
default:
break;
}
}
protected void restartApp() {
Intent intent = new Intent(this, LaunchActivity.class);
intent.putExtra(Constants.START_LAUNCH_ACTION,Constants.STATUS_FORCE_KILLED);
startActivity(intent);
}
}
3、app引导页面
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//这里设置为app正常态
AppUtils.getInstance().setAppStatus(Constants.STATUS_NORMAL);
}
4、app的首页
注意app的首页的启动模式一定要设置为singleTask
模拟系统回收
app运行在后台,android系统会在内存不够用的时候,回收运行在后台的app,它有以下三种作用。
- 不保留app内存数据(全局的单例等),
- 保留activity堆栈数据,
- 再次开启app,会调用onCreate()重新创建栈顶的activity,
模拟:
- terminal application
- kill掉进程
1、 按Home按键退出你的程序;
2、
# 找到该APP的进程ID
adb shell ps
# 找到你APP的报名
adb shell ps | grep your.app.package
# 按照上述命令操作后,看起来是这样子的:
# USER PID PPID VSIZE RSS WCHAN PC NAME
# u0_a198 21997 160 827940 22064 ffffffff 00000000 S your.app.package
# 通过PID将你的APP杀掉
adb shell kill 21997
# APP现在被杀掉啦
不保留活动
android 系统在开发者选项中,有一个不保留活动的选项,它有以下三种作用。
- 保留app内存数据(全局的单例等),
- 保留activity堆栈数据,
- 再次开启app,会调用onCreate()重新创建栈顶的activity,