APP在後臺被系統回收後,如何重新啟動
問題:
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,