# 對 Android 應用被強殺重回應用的優化處理(重走應用流程)

關鍵詞:強殺 / home 鍵 / static 導致的 NullPointerException / BaseActivity

背景:Android 編程中我們經常會使用到 static 變量,static 變量屬於類本身,所有實例調用的靜態變量的值都是一樣的,如果在某一個類裏改變了一個靜態變量的值,其它所有的實例在調用這個值的時候也全都會發生了變化。static 在虛擬機中單獨佔用內存,在不同的包和類中都能使用,很方便。但是當應用被強殺後,若應用較長時間處於後臺,會導致 NullPointerException 的異常產生。

解釋:因爲按下 Android 的 home 鍵,如果位於後臺較長時間,或者由於內存不足應用被強殺,應用依然會保持 activity 的棧信息(activity 棧沒有被清空,比如說 A -> B -> C -> D 這個棧還保存了,只是 ABCD 這幾個 activity 實例沒有了。所以回到 App 時,顯示的還是 D 頁面),當我們選擇 “最近打開的應用” 回到前臺的時候,該 activity 會重新執行 onCreate() 進行初始化操作(也包括 application 的初始化),如果操作中包含了對其他類的靜態變量的引用,而應用被強殺後該靜態變量的實例已被虛擬機回收,這樣便引發了空指針。
那麼問題來了,我們理應重新走應用的流程,如何改善這種情況而避免這種異常的發生呢,既然 App 都被強殺了,幹嘛不重新走第一次啓動的流程呢,別讓 App 回到 D 而是再啓動 A,這樣所有的變量都是按正常的流程去初始化,也就不會空指針了。需要判斷是否被強殺,如果是,就強制重新走應用的開始流程。通過在有心課堂的學習,進行了這個過程的模仿並梳理了思路。

參考:應用被強殺了怎麼辦 http://notes.stay4it.com/2016/02/26/how-to-handle-app-force-killed/

流程梳理 #

自定義了一個 CustomApplication,用來初始化全局變量

public class CustomApplication extends Application {

    public static ArrayList<String> mTestNullPointer;
    // 我們把 -1 模擬表示被強殺
    public static int mAppStatus = -1;

    @Override
    public void onCreate() {
        super.onCreate();
    }
}

做了一個父類 BaseActivity ,讓每一個 Activity 都繼承自 BaseActivity,各 Activity 要麼會執行 protectApp() 方法,要麼會執行 setupData() 方法,前者是由於強殺,後者是正常情況下的初始化操作。

public class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 判斷如果被強殺,就回到 HomeActivity 中去,否則可以初始化
        if (CustomApplication.mAppStatus == -1) {
            // 重走應用流程
            protectApp();
        } else {
            setupData();
        }

    }

    // 在這裏做初始化操作
    protected void setupData() {

    }

    // 使用 protected 讓子類可以重寫該方法
    protected void protectApp() {
        // 重新走應用的流程是一個正確的做法,因爲應用被強殺了還保存 Activity 的棧信息是不合理的
        Intent intent = new Intent(this, HomeActivity.class);
        intent.putExtra("action", "force_kill");
        startActivity(intent);
    }

}

下面是模擬強殺並且進行優化處理的做法流程

對 Android 應用被強殺重回應用的優化處理(重走應用流程)

圖中各 Activity 對應的代碼如下

1、WelcomeActivity

public class WelcomeActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // 把狀態變爲 0 能使父類不會走 protectApp()
        CustomApplication.mAppStatus = 0;
        super.onCreate(savedInstanceState);
    }

    @Override
    protected void setupData() {
        setContentView(R.layout.activity_welcome);
        handler.sendEmptyMessageDelayed(0, 1000);
    }

    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            startActivity(new Intent(WelcomeActivity.this, LoginActivity.class));
            finish();
        }
    };
}

2、LoginActivity

public class LoginActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
    }

    public void login(View view) {
        startActivity(new Intent(this, HomeActivity.class));
    }
}

3、HomeActivity

/**
 * HomeActivity 的啓動模式爲 "singleTask"
 */

public class HomeActivity extends BaseActivity implements View.OnClickListener {

    private Button mHomeProfileBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    // 初始化操作
    @Override
    protected void setupData() {
        setContentView(R.layout.activity_home);
        mHomeProfileBtn = $(R.id.id_mHomeProfileBtn);
        mHomeProfileBtn.setOnClickListener(this);
        CustomApplication.mTestNullPointer = new ArrayList<>();
        CustomApplication.mTestNullPointer.add("profile");
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        String action = intent.getStringExtra("action");
        if ("force_kill".equals(action)) {
            // 在 ProfileActivity 被強殺了就重新走應用的流程
            protectApp();
        }
    }

    @Override
    protected void protectApp() {
        // 回到 WelcomeActivity
        startActivity(new Intent(this, WelcomeActivity.class));
        finish();
    }

    @Override
    public void onClick(View view) {
        startActivity(new Intent(this, ProfileActivity.class));
    }

    @SuppressWarnings("unchecked")
    private <T> T $(int resId) {
        return (T) findViewById(resId);
    }
}

4、ProfileActivity

public class ProfileActivity extends BaseActivity {

    private TextView mProfileLabel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 讓所有繼承於 BaseActivity 的子類的 onCreate() 都直接交給父類 BaseActivity 去操作,
        // 讓父類進行一系列的先判斷,不讓子類隨隨便便地進行初始化
    }

    @Override
    protected void setupData() {
        setContentView(R.layout.activity_profile);
        mProfileLabel = $(R.id.id_mProfileLabel);
        mProfileLabel.setText(CustomApplication.mTestNullPointer.toString());
    }

    @SuppressWarnings("unchecked")
    private <TT> TT $(int resId) {
        return (TT) findViewById(resId);
    }

}

End.

Note by HF.
Learn from 有心課堂


發佈了104 篇原創文章 · 獲贊 63 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章