Wednesday, September 17, 2014

不要過度依賴Activity.onDestroy()去執行程式

文章攢寫時間︰2014/09/17 15:00

一、問題

今天遇到一個crash,
DVM由於Android背景執行的程式過多,
可執行app的記憶體不足,
直接將我服務進程砍掉(kill process),
然而我在Activity.onDestroy()裡做了很多值的還原或歸零(預設值)的動作,
因為這種方式,
導致所有該還原回預設值的動作,
通通沒做

二、解決辦法

這件事算是學了個經驗,
開發者無法預期使用者一定會使用返回鍵(Back button)將程式正常關閉,
因為可能有很多"外在"因素會造成你的程式被強制終止。

所以,
不要在Activity.onDestroy()做許多值還原或歸零(預設值)的動作,
因為你根本無法預期這些行為在低階手機裡是能被正確執行的。

三、其它

static變數在Android裡的生命週期

以下內容來源︰stackoverflow

Lets start with a bit of background: What happens when you start an application?
我們來談一些關於android背後的機制︰當你啟動了一個應用程式(application)後,背後究竟發生了些什麼事?

The OS starts a process and assigns it a unique process id and allocates a process table.A process start an instance of DVM(Dalvik VM); Each application runs inside a DVM.
在啟動了一個應用程式以後,作業系統開啟了一個進程(process),並同時給這個進程一個專屬進程id,並分配給進程一個進程資料表(process table,用來儲存該進程的所有相關資訊)。然後,這個進程會啟動一個DVM(Dalvik VM,Dalvik虛擬機)實體,每一個應用程式(application)都分別運作在各自的DVM裡。

A DVM manages class loading unloading, instance lifecycle, GC etc.
DVM管理著每個class是否要被載入(loading unloading)、生命週期、實體、資源回收(Garbage Collection)等等。

Lifetime of a static variable: A static variable comes into existence when a class is loaded by the JVM and dies when the class is unloaded.
static變數的生命週期︰當一個class被JVM載入後,static變數值就會被產生,當class被卸載後,static變數值就會被消毀。

So if you create an android application and initialize a static variable, it will remain in the JVM until one of the following happens:
1. the class is unloaded
2. the JVM shuts down
3. the process dies

Note that the value of the static variable will persist when you switch to a different activity of another application and none of the above three happens. Should any of the above three happen the static will lose its value.
所以,當你建立了一個android應用程式並且初始化了static變數後,這個static值就會被保留直到下列事件發生為止︰
1.class被卸載
2.JVM被關閉
3.進程(process)被消滅了
備註︰當你切換到不同的應用程式的Activity時,原Activity的static變數都會一直被保留,除非上述的事件被觸發。一旦被觸發後,static變數就會遺失原來的值。

You can test this with a few lines of code:
1.print the uninitialized static in onCreate of your activity -> should print null
2.initialize the static. print it -> value would be non null
3.Hit the back button and go to home screen. Note: Home screen is another activity.
4.Launch your activity again -> the static variable will be non-null
5.Kill your application process from DDMS(stop button in the devices window).
6.Restart your activity -> the static will have null value.
你可以用下列的方式試試上述的理論︰
1.在Activity onCreate()函式裡印岀未初始化的static變數值-->應該印岀null。
2.初始化static變數值-->印岀來的值應該就不是null值了。
3.點擊手機返回鍵並回到手機Home首頁(備註︰Home首頁就是另一個Activity)。
4.重啟你的Activity-->static變數值理論上不會是null。
5.從DDMS砍掉你的應用程式進程(在Eclipse ADT裡有一個Devices視窗,裡面有stop按鈕,見下圖)。
6.重啟你的Activity-->static變數值將會變成null。
Devices視窗裡的Stop按鈕。

如果看不到這個視窗,請在Eclipse工具列[Window]-->[Show View]-->[Other]-->[Android]-->選擇[Devices]即可岀現該Devices視窗。

7 comments:

CITYWALKER said...

讚!!!


特別是static部分
解決了我一直想要靠「邊做邊體驗」來釐清的問題

記憶體不夠用的部分
我會將自己的物件設計成「可以快速呼叫、快速拋棄」的型態
例如一個函數 如果只有在特定區段會被大量使用(少量使用) 我會把它做成一個物件 要用時在宣告實體 不用了就丟掉
宣告的瞬間會犧牲一些效能 但在Java/Android中可以有效解決這個問題...(經驗中 這招幾乎沒失敗過)

潘小鰻 said...

謝謝CITYWALKER,很有用的實務分享。

蘇小毛 said...

請問 小鰻
假如在app內,使用了許多的static 變數(如String .Array.int) 是否對app的記憶體有影響,而導致OOM的問題呢?

許多開發者建議可以使用applicaton 變數來儲存資料,想請教 application variable 跟 static 的不同?

謝謝您分享的知識!感謝

潘小鰻 said...

Hello,小毛,
以我個人的實務經驗來看,許多的static變數使用並沒有對app記憶體有太多影響,遇過的OOM發生比較多的原因是以前Android過量使用Drawable。
我沒有在聽過application變數這個術語,方便分享一下您的認知嗎?
謝謝

蘇小毛 said...

http://inchoo.net/dev-talk/android-development/android-global-variables/

上面的Link 是關於 變數寫在application 的使用方式,概念跟static 差不多

只是application 似乎是android的原生東西,所有有人建議將變數寫在appliction

潘小鰻 said...

@蘇小毛 謝謝你,很有趣的用法。
這部份已經超過我回答的能力,恕目前無法回應您~

CITYWALKER said...

剛好 這幾天遭遇到這個問題

這個問題是這樣:
我在一個Fragment中設置一個View元件的大小 並且在建構子中定義了「大小」的static參數
可是我發現這個「大小」的static參數不起作用 (永遠是預設值)
因為 Activity會在Fragment的建構子之前執行Fragment設置畫面的函數/程序/程式碼

想像一下 挺奇妙的
因為設置畫面的函數/程序/程式碼並不是static

所以Android一般建議將static參數設置在Application中
如果放在Application以外的地方
即使你在怎麼努力確保這個物件會被import (static區段會被執行)
基於Android的特性 (加上版主的經驗) 顯然不能保證static參數不會被突發性還原、或是還原後能否正常設置... 一切都是有風險的