在一個App裏面總有一些數據須要在多個地方用到。這些數據多是一個 session token,一次費時計算的結果等。一般爲了不activity之間傳遞對象的開銷 ,這些數據通常都會保存到持久化存儲裏面。html
有人建議將這些數據保存到 Application 對象裏面,這樣這些數據對全部應用內的activities可用。這種方法簡單,優雅並且……徹底扯淡。android
假設把你的數據都保存到Application對象裏面去了,那麼你的應用最後會以一個NullPointerException 異常crash掉。shell
Application 對象:緩存
// access modifiers omitted for brevity
class MyApplication extends Application {
String name;
String getName() {
return name;
}
void setName(String name) {
this.name = name;
}
}
第一個activity,咱們往application對象裏面存儲了用戶姓名:網絡
// access modifiers omitted for brevity
class WhatIsYourNameActivity extends Activity {
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.writing);
// Just assume that in the real app we would really ask it!
MyApplication app = (MyApplication) getApplication();
app.setName("Developer Phil");
startActivity(new Intent(this, GreetLoudlyActivity.class));
}
}
第二個activity,咱們調用第一個activity設置並存在application裏面的用戶姓名:session
// access modifiers omitted for brevity
class GreetLoudlyActivity extends Activity {
TextView textview;
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.reading);
textview = (TextView) findViewById(R.id.message);
}
void onResume() {
super.onResume();
MyApplication app = (MyApplication) getApplication();
textview.setText("HELLO " + app.getName().toUpperCase());
}
}
用戶啓動app。app
在 WhatIsYourNameActivity裏面,要求用戶輸入姓名,並存儲到 MyApplication。ide
在 GreetLoudlyActivity裏面,你從MyApplication 對象中得到用戶姓名,而且顯示。測試
用戶按home鍵離開這個app。ui
幾個小時後,Android系統爲了回收內存kill掉了這個app。到目前爲止,一切尚好。接下來就是crash的部分了…
用戶從新打開這個App。
Android系統建立一個新的 MyApplication 實例並恢復 GreetLoudlyActivity。
GreetLoudlyActivity 重新的 MyApplication 實例中獲取用戶姓名,可獲得的爲空,最後致使NullPointerException。
在上面這個例子中,app會crash得緣由是這個 Application 對象是全新的,因此這個name 變量裏面的值爲 null,當調用String#toUpperCase() 方法時就致使了NullPointerException。
整個問題的核心在於:application 對象不會一直呆着內存裏面,它會被kill掉。與你們廣泛的見解不一樣之處在於,實際上app不會從新開始啓動。Android系統會建立一個新的Application 對象,而後啓動上次用戶離開時的activity以形成這個app歷來沒有被kill掉得假象。
你覺得你的application能夠保存數據,卻沒想到你的用戶在沒有打開activity A 以前就就直接打開了 activity B ,因而你就收到了一個 crash 的 surprise。
這裏沒啥神奇的解決方法,你能夠試試下面幾種方法:
直接將數據經過intent傳遞給 Activity 。
使用官方推薦的幾種方式將數據持久化到磁盤上。
在使用數據的時候老是要對變量的值進行非空檢查。
進行本地數據保護,對apk應用的網絡緩存、本地存儲數據進行深度保護。
更新: Daniel Lew指出,kill app更簡單的方式就是使用DDMS裏面「中止進程」 。你在調試你的應用的時候可使用這招。
爲了測試這個,你必須使用一個Android模擬器或者一臺root過的Android手機。
使用home按鈕退出app。
在終端裏:
# find the process id
adb shell ps
# then find the line with the package name of your app
# Mac/Unix: save some time by using grep:
adb shell ps | grep your.app.package
# The result should look like:
# USER PID PPID VSIZE RSS WCHAN PC NAME
# u0_a198 21997 160 827940 22064 ffffffff 00000000 S your.app.package
# Kill the app by PID
adb shell kill -9 21997
# the app is now killed
長按home按鈕回到以前的app。
你如今是出於一個新的application實例中了。
不要在application對象裏面儲存數據,這容易出錯,致使你的app crash。
要麼將你後面要用的數據保存到磁盤上面或者保存到intent得extra裏面直接傳遞給activity 。
這些結論不但對application對象有用,對你app裏面的單例對象(singleton)或者公共靜態變量(public static)一樣適用。