譯自:http://www.developerphil.com/dont-store-data-in-the-application-object/
千萬不要把數據存儲在Application對象中
may 5, 2013
在我們的應用程序中有些數據需要在多處使用。有可能是一個會話令牌,花費很大代價才得來的結果,等等。而且我們總是想避免在兩個Activity之間傳遞數據或者不想把數據存儲在持久存儲器中。
一個解決問題的模式是把數據存儲在應用的Application對象中,這樣呢存儲的數據就能在所有的Activity中共享。這種解決辦法看起來很簡單,很優雅,但其實是錯誤的!
如果你始終相信數據一直會待在Application對象中,那麼你的應用最終會因爲NullPointerException異常而終止。
一個很簡單的測試案例
The Application object:
// access modifiers omitted for brevity
classMyApplication extends Application {
String name;
String getName() {
returnname;
}
voidsetName(String name) {
this.name = name;
}
}
The first activity, where we store the name of the user in the application object:
// access modifiers omitted for brevity
classWhatIsYourNameActivity extends Activity {
voidonCreate(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(newIntent(this, GreetLoudlyActivity.class));
}
}
The second activity, where we shout the name of the user:
// access modifiers omitted for brevity
classGreetLoudlyActivity extends Activity {
TextView textview;
voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.reading);
textview = (TextView) findViewById(R.id.message);
}
voidonResume() {
super.onResume();
MyApplication app = (MyApplication) getApplication();
textview.setText("HELLO " + app.getName().toUpperCase());
}
}
用戶使用你應用可能的場景
1、用戶打開你的應用
2、在WhatIsYourNameActivity中,你把用戶的名字存儲在MyApplication對象中
3、在GreetLoudlyActivity中,你從MyApplication對象中把用戶的名字提取出來並顯示給用戶
4、用戶按下home鍵離開了你的應用
5、在幾個小時後,Android可能會在後臺默默地把你的應用殺死,釋放內存給其它應用使用
到目前爲止並不會出現啥問題,你的應用也沒有出現任何異常
6、用戶重新打開了你的應用
7、Android會重新創建一個MyApplication對象和恢復GreeLoudlyActivity
8、GreetLoudlyActivity獲取用戶的名字,但現在用戶名字已經是null,你的應用拋出NullPointerException異常終止
爲什麼會異常終止
在例子程序中,由於Application對象是新創建出來的,name屬性爲null,調用String#toUpperCase()
導致NullPointerException
發生問題的核心:Application對象不會永遠存在於內存中,可能會被回收殺死。普遍的看法是應用不會從頭重新啓動。Android會創建一個新的Application對象並且恢復之前的Activity,給用戶一個錯覺是應用永遠都不會被殺死,一直停留在原先的狀態
這意味着如果你期待用戶不會在打開Activity A之前打開Activity B,數據一直會在Application對象中。那麼你會得到一個異常終止的驚喜!
解決辦法
這裏沒有很好的解決方案,但是你可以遵循以下一些準則:
1、使用intent明確的在Activity之間傳遞數據
2、使用many
ways 中的一種來在磁盤上存儲數據
3、總是手動來進行null-check
怎樣來模擬應用程序被殺
EDIT:Daniel
Lew 指出一個最簡單的方法是在DDMS中使用“Stop Process”功能來殺死一個應用(設備在debug模式)
要測試這個,你必須需要有一個模擬器或一臺root過的手機
1、使用home鍵來退出你的應用
2、在命令行敲入如下命令
# 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
3、使用任務切換功能重新回到你的應用,現在你的應用就重新創建了Application對象
總結
在Application對象中存儲數據容易出錯,讓你的應用異常終止
如果數據在後面會被使用到,你可以存儲在磁盤中或者通過intent’s extras傳遞到需要的Activity中
另外還要注意的是對於Application對象來說是這種情況,那麼對於任何單例對象或者靜態屬性來說都如此
注:經過自己的測試,確實出現如作者所描述的情況