2013年5月23日 星期四

Android AsyncTask 寫成獨立class 並且不讓程式邏輯跳來跳去

用這樣的寫法可以將開始AsyncTask 和AsyncTask結束後要如何更新ui的code寫在一起.

避免程式破碎.



strategy pattern:

因為java沒有function pointer, 這裡就把strategy pattern當作function pointer來用




首先建立一個interface之後做call back用:


public interface MyCallBack {
public void onTaskComplete(Object p);
}


建立一個class 繼承AsyncTask


public class DownloadTask extends AsyncTask<Void, Void, Void> {

    MyCallBack callBack;
    ReturnObject ret;   //耗時工作的回傳值    
    public DownloadPackageListTask(MyCallBack pCallBack) {
    callBack = pCallBack;
}
    
@Override
protected Void doInBackground(Void... params) {

ret = (網路傳輸之類的function,最後得到回傳值)

return null;
}


@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
callBack.onTaskComplete(ret); 
}
}


有了以上一個interface跟一個asynctask的subcalss後,我們就可以在main thread 中這樣使用:


final DownloadTask task = new DownloadTask(
new MyCallBack() { //藉由MyCallBack interface建立一個 anonymous 的strategy class

@Override
public void onTaskComplete(Object p) {
ReturnObject ro = (ReuturnObject)p;

(可以將得到return object後的ui code直接寫在這裡, 這個block內的東西將會在asynctask 的 onPostExecute中執行)

});

task.execute();



如此就可以直接把開始執行耗時工作和執行完耗時工作要做的事情寫在一起了!

2013年5月15日 星期三

Android 恐怖的Out of memory exception 之:死不了的Activity


當初遇到這個問題debug超久直到看完了以下google io影片後才有眉目,

http://www.youtube.com/watch?v=_CruQY55HOk

此篇為此影片的subset, 有閒的推薦直接看影片 (以下圖片來源為影片截圖)


故事是從這段外表看似簡單,leak卻過於常人的code開始的


狀況敘述:

Android 的Garbage collector只有在物件沒有被任何別的物件reference到的時候才回將這個物件回收.

當上圖這個Activity離開的時候理當被Garbege collector回收,但是因為leak物件依然reference這個Activity,所以此Activity不會被回收,

若同時這個Activity reference到一些Bitmap的話, 則這些Bitmap也不會被回收掉!!

導致多start幾次這個activity馬上就會噴out of memory.



狀況詳解:
問題的關鍵在於1.Leaky為一個inner class, 且2.leak為static object.

這兩件事情一同造成了MainActivity不會被garbage collect:

1. java non-static inner class 在實例化的時候固定會有一個reference指向其enclose class(包住這個inner class的class)

這個特性是為了能在inner class中使用其enclose class中的資源,例如
public class MainActivity extends Activity
{
 class Leaky
 {
     void doSomething()
  {
    MainActivity.this.someFunction();
  }
 }
}

這就是為什麼上圖中leaky物件指向MainActivity.

2. java class中的static variable 會在java class loader load class 後儲存在heap中一段稱為Permanent Genration的特如區域中. 而這區域在process結束之前都不會被回收.

因為leak物件為static的關係所以

leak = new Leaky() 這行程式碼建立了上圖中MainActivity class 到leaky的連結.

結果因為leak不會被回收導致被leak reference到的MainActivity就算離開了也還是存在著一條reference而不會被回收.

同樣的情況不只會發生在inner class, anonymous class一樣會產生一條對enclose class的連結,所以也可能會導致一樣的狀況


一般使用static object是不會出問題的, 但是要特別注意這個static object是否為一個inner class或anonymous class.

很多的memory leak都跟static脫不了關西, static與singleton雖然好用,但使用上要特別小心.



2013年5月13日 星期一

Android智障的oom(out of memory)原因: 圖片讀進來超大!? 原來是放錯資料夾

這問題雖然低能, 但我相信一定很多人不知道, 卻又深受其苦.

Android 在load image的時候會自動做scale的動作, 舉個例子

如果我把圖片放在 drawable-ldpi或drawable中, 卻使用被歸類於hdpi的手機來讀這張圖片,

則在圖片decode的時候會順便再多做scale的動作.

像我這種懶人開發的時候通常只會針對一種resolution生一組圖片然後把它隨便找一個資料夾放, 

例如開一個drawable資料夾然後全都放裡面, 跑起來有看到圖就以為沒差. 

然後每次oom都在想android是搞毛啊沒幾張圖就亂噴.

其實是當代的手機幾乎都是hdpi以上,所以如果在drawable-ldpi中放大resolution的圖片,

則在圖片decode的時候會被沒有必要的scale到超級大. 所以memory很容易就爆了.

懶人解法是將圖片全部放到drawable-xdpi下面, 就能保證只會scale down了.



在網路上survey了很久, 有一堆解法都是自己在loading bitmap的時候手動scale, 不然就是自己做memory 控管寫了一堆扣.  結果搞了半天才發現是放對資料夾就沒事了.

話說雖然在android sdk document中似乎有提到會自動scale, 但誰會一個字一個字看完document在開始寫啊= =+

  

以上解法雖然輕鬆愉快, 但依然會有memory的浪費, 因為放進project的圖片不可能完全適合每一款手機的螢幕手機,

android document 中有提到如何在load bitmap的時候針對實際需求去scale


其中的 Loading Large Bitmaps Efficiently 段落就是在說這個.
流程大概是先讀出image的大小 -> 再根據實際需要大小算出比例 -> 在實際decode bitmap的時候做scale.  雖然不難但是有點煩.

我個人只覺得只要能避免掉將high resolution image 放到 較低的資料夾所導致的多餘scale
對基本app來說已經很足夠了 ^.<

2013年5月12日 星期日

Start

本來不覺得有什麼寫blog的需求, 打電動都來不及了.

但最近對自己殘弱的記憶力已經到達忍無可忍的程度了QQ

每次要寫麼function明明不久前才剛用過,卻要整個重新survey一遍,

更恐怖的是花的時間還不下之前, 簡直坑爹

下定決心把每次辛苦compile過的survey結果紀錄下來!