1. 程式人生 > >APP在後臺被系統回收後,如何重新啟動

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,