应用被系统回收导致崩溃的解决方案
应用长时间被搁置后台,如果应用进程被系统回收(内存不足),重新打开应用时,会造成静态变量空指针,进而应用崩溃。
有以下简单的示例代码:
BaseApplication
public class BaseApplication extends Application {
public static int forceKill = -1;
public static List<String> news;
@Override
public void onCreate() {
super.onCreate();
}
public static void initNews() {
List<String> news = new ArrayList<>();
news.add("1");
news.add("21");
news.add("321");
BaseApplication.news = news;
}
MainActivity(Launcher)
public class MainActivity extends Activity {
private Button btnJump;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnJump = findViewById(R.id.btn_jump);
//正常启动,置为0
forceKill = 0;
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.d("log", "MainAct onRestoreInstanceState");
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.d("log", "MainAct onSaveInstanceState");
}
@Override
protected void onResume() {
super.onResume();
btnJump.setText("Jump Jump");
initNews();
}
public void jump(View view) {
Intent intent = new Intent(this, NewsActivity.class);
startActivity(intent);
}
}
NewsActivity
public class NewsActivity extends Activity {
private Button btnNews;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_news);
btnNews = findViewById(R.id.btn_news);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
news = savedInstanceState.getStringArrayList("news");
Log.d("log", "NewsAct onRestoreInstanceState");
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.d("log", "NewsAct onSaveInstanceState");
/*如果不对数据做缓存,恢复页面的时候变量news空指针崩溃*/
outState.putStringArrayList("news", (ArrayList<String>) news);
}
@Override
protected void onResume() {
super.onResume();
Log.d("log", "NewsAct onResume : IsForceKilled ? " + (forceKill == -1));
btnNews.setText("News: " + news.get(0));
}
}
复原这个问题的操作流程和日志
操作:
Launched App.
跳转 NewsAct.
打印日志:
D/log: NewsAct onResume : IsForceKilled ? false
D/log: MainAct onSaveInstanceState
操作:
按Home键.
打印日志:
D/log: News Act onSaveInstanceState:
操作:
点击LogCat中 [Terminate Application] 按钮,回收App所在进程.
然后点击App桌面图标重新启动.
打印日志:
D/log: NewsAct onRestoreInstanceState
D/log: NewsAct onResume : IsForceKilled ? true
操作:
按返回键
打印日志:
D/log: MainAct onRestoreInstanceState
D/log: MainAct onResume
显然,[Terminate Application] 按钮回收掉了App进程,重新代开应用,系统需要进行补偿,即恢复之前最后停留的页面NewsActivity。如果在NewsActivity页面中没有对集合变量 [news] 做缓存,因为不会经过MainActivity页面,[news] 将会造成空指针崩溃。
方案,对页面可能会造成这一崩溃现象的变量值进行缓存,以便于真的因为系统原因而造成的应用被回收重启恢复页面时不至于崩溃。
还有就是,尽可能的避免在页面中使用静态变量,如果需要使用到静态变量,则应该执行上面的方案,即进行值缓存。
如果应用中已经大量使用了静态变量,根本不想一个一个改(祖传代码改不得),还有最后一招。
就是判断强杀标志满足: isForceKilled == -1,然后主动重启:
public static void safeReLauncher() {
BaseApplication application = BaseApplication.getApplication();
Intent intent = application.getPackageManager().getLaunchIntentForPackage(application.getPackageName());
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent restartIntent = PendingIntent.getActivity(application.getApplicationContext(), 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);
//退出程序
AlarmManager mgr = (AlarmManager) application.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, 100, restartIntent); // 100毫秒后重启应用
//结束进程之前可以把你程序的注销或者退出代码放在这段代码之前
android.os.Process.killProcess(android.os.Process.myPid());
}