關(guān)鍵詞:強(qiáng)殺 / home 鍵 / static 導(dǎo)致的 NullPointerException / BaseActivity
背景:Android 編程中我們經(jīng)常會(huì)使用到 static 變量,static 變量屬于類本身,所有實(shí)例調(diào)用的靜態(tài)變量的值都是一樣的,如果在某一個(gè)類里改變了一個(gè)靜態(tài)變量的值,其它所有的實(shí)例在調(diào)用這個(gè)值的時(shí)候也全都會(huì)發(fā)生了變化。static 在虛擬機(jī)中單獨(dú)占用內(nèi)存,在不同的包和類中都能使用,很方便。但是當(dāng)應(yīng)用被強(qiáng)殺后,若應(yīng)用較長(zhǎng)時(shí)間處于后臺(tái),會(huì)導(dǎo)致 NullPointerException 的異常產(chǎn)生。
解釋:因?yàn)榘聪?Android 的 home 鍵,如果位于后臺(tái)較長(zhǎng)時(shí)間,或者由于內(nèi)存不足應(yīng)用被強(qiáng)殺,應(yīng)用依然會(huì)保持 activity 的棧信息(activity 棧沒有被清空,比如說 A -> B -> C -> D 這個(gè)棧還保存了,只是 ABCD 這幾個(gè) activity 實(shí)例沒有了。所以回到 App 時(shí),顯示的還是 D 頁(yè)面),當(dāng)我們選擇 “最近打開的應(yīng)用” 回到前臺(tái)的時(shí)候,該 activity 會(huì)重新執(zhí)行 onCreate() 進(jìn)行初始化操作(也包括 application 的初始化),如果操作中包含了對(duì)其他類的靜態(tài)變量的引用,而應(yīng)用被強(qiáng)殺后該靜態(tài)變量的實(shí)例已被虛擬機(jī)回收,這樣便引發(fā)了空指針。 那么問題來了,我們理應(yīng)重新走應(yīng)用的流程,如何改善這種情況而避免這種異常的發(fā)生呢,既然 App 都被強(qiáng)殺了,干嘛不重新走第一次啟動(dòng)的流程呢,別讓 App 回到 D 而是再啟動(dòng) A,這樣所有的變量都是按正常的流程去初始化,也就不會(huì)空指針了。需要判斷是否被強(qiáng)殺,如果是,就強(qiáng)制重新走應(yīng)用的開始流程。通過在有心課堂的學(xué)習(xí),進(jìn)行了這個(gè)過程的模仿并梳理了思路。
參考:應(yīng)用被強(qiáng)殺了怎么辦 http://notes.stay4it.com/2016/02/26/how-to-handle-app-force-killed/
自定義了一個(gè) CustomApplication,用來初始化全局變量
public class CustomApplication extends Application { public static ArrayList<String> mTestNullPointer; // 我們把 -1 模擬表示被強(qiáng)殺 public static int mAppStatus = -1; @Override public void onCreate() { super.onCreate(); }}做了一個(gè)父類 BaseActivity ,讓每一個(gè) Activity 都繼承自 BaseActivity,各 Activity 要么會(huì)執(zhí)行 PRotectApp() 方法,要么會(huì)執(zhí)行 setupData() 方法,前者是由于強(qiáng)殺,后者是正常情況下的初始化操作。
public class BaseActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 判斷如果被強(qiáng)殺,就回到 HomeActivity 中去,否則可以初始化 if (CustomApplication.mAppStatus == -1) { // 重走應(yīng)用流程 protectApp(); } else { setupData(); } } // 在這里做初始化操作 protected void setupData() { } // 使用 protected 讓子類可以重寫該方法 protected void protectApp() { // 重新走應(yīng)用的流程是一個(gè)正確的做法,因?yàn)閼?yīng)用被強(qiáng)殺了還保存 Activity 的棧信息是不合理的 Intent intent = new Intent(this, HomeActivity.class); intent.putExtra("action", "force_kill"); startActivity(intent); }}下面是模擬強(qiáng)殺并且進(jìn)行優(yōu)化處理的做法流程

圖中各 Activity 對(duì)應(yīng)的代碼如下
1、WelcomeActivity
public class WelcomeActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { // 把狀態(tài)變?yōu)?0 能使父類不會(huì)走 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 的啟動(dòng)模式為 "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 被強(qiáng)殺了就重新走應(yīng)用的流程 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 去操作, // 讓父類進(jìn)行一系列的先判斷,不讓子類隨隨便便地進(jìn)行初始化 } @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 有心課堂
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注