之前在 Save ArrayList to SharedPreferences 提到過,如何將 ArrayList 物件存到 SharedPreferences 裡,最近看到一個更棒的方式,那就是使用 Gson。
假設我現在有個 MyObject 物件想要存起來,可以這樣寫:
要取出來時:
回到之前 ArrayList 的問題,儲存可以改用上面範例來處理,至於取出的方法需要改寫成這樣:
比起原本的程式碼,變得乾淨俐落許多!
Reference:
http://stackoverflow.com/questions/5418160
2015/08/18
2015/04/28
Test Your Deep Links
今天看文件才想到 adb 有提供指令可以直接發 Intent 給 App,而且還能帶 URI data,這樣要用來測試 protocol 就方便多了!
Reference:
https://developer.android.com/training/app-indexing/deep-linking.html#testing-filters
Reference:
https://developer.android.com/training/app-indexing/deep-linking.html#testing-filters
2015/04/27
android.database.CursorWindowAllocationException
當 Cursor 沒有確實關閉並且累積到一定數目時,App 就會出現異常報錯,無法查詢資料庫,甚至 crash。這邊記錄一些容易出錯的例子:
。提前返回
有時會沒注意到在調用 close() 之前就 return 了,尤其在比較複雜的程式碼中就可能會疏忽。
。異常處理
在還沒執行 close() 之前就因為處理 exception 而跳過。
要避免這種情形,應該要把 close() 放到 finally 裡確保它會執行。
另外我們也可以在 Cursor 的建構及關閉時加上 log 來測試及偵錯。
這樣就能清楚檢查是否有 Cursor 未確實關閉,但要先等 App 完全結束程序時再做,因為有些 Cursor 可能仍在背景運行中。
Reference:
http://oteku.blogspot.tw/2013/11/how-to-detect-android-cursor-leak-en.html
。提前返回
有時會沒注意到在調用 close() 之前就 return 了,尤其在比較複雜的程式碼中就可能會疏忽。
。異常處理
在還沒執行 close() 之前就因為處理 exception 而跳過。
要避免這種情形,應該要把 close() 放到 finally 裡確保它會執行。
另外我們也可以在 Cursor 的建構及關閉時加上 log 來測試及偵錯。
這樣就能清楚檢查是否有 Cursor 未確實關閉,但要先等 App 完全結束程序時再做,因為有些 Cursor 可能仍在背景運行中。
Reference:
http://oteku.blogspot.tw/2013/11/how-to-detect-android-cursor-leak-en.html
2015/04/26
java.util.ConcurrentModificationException
前陣子在做 for-each 執行某項過濾功能時遇到這個異常報錯,原因在於 for-each 的架構是不能使用於過濾需求的,一旦變動了集合的內容便會發生錯誤。在這邊可以使用 Iterator 調動集合的方式來解決,在多個執行緒中也能做到同步。
另外如果集合不大,也可以使用 counted loop 來處理。
Reference:
http://stackoverflow.com/a/223929/1770125
另外如果集合不大,也可以使用 counted loop 來處理。
Reference:
http://stackoverflow.com/a/223929/1770125
2015/03/01
POJO
POJO,全名為 Plain Old Java Object。它是一個簡單的 Java Object 概念定義,可以包含邏輯處理,以下三種情況不能稱作 POJO:
1. 繼承 Java frameworks 的 class
2. 實作 Java frameworks 的 interface
3. 包含 Java frameworks 的 annotation
Reference:
http://martinfowler.com/bliki/POJO.html
http://en.wikipedia.org/wiki/Plain_Old_Java_Object
1. 繼承 Java frameworks 的 class
2. 實作 Java frameworks 的 interface
3. 包含 Java frameworks 的 annotation
Reference:
http://martinfowler.com/bliki/POJO.html
http://en.wikipedia.org/wiki/Plain_Old_Java_Object
Programmatically Declare Styleable In Custom View
在我們設計 custom View 的時候,一般都會想要定義一些專屬的 attributes 放在 attrs.xml 裡面,方便之後可以調整它的參數來因應 layout 規格。例如在 Chris Banes 的 FloatLabelLayout 中,他先在 attrs.xml 定義了每個方向的 padding,以及要用作 label 的 hint 和 TextAppearance,
然後在 FloatLabelLayout.java 取得外部利用這些 attributes 所設定的數值,進而調整裡面需要變動的元件。
但是有些時候,我們可能想要直接在程式碼中定義這些行為,那該如何實作呢?
在這個例子中,我們先宣告了 styleable 這個 int array,放入需要用到的 attributes,接著調用 Context#obtainStyledAttributes() 傳回一個 TypedArray 的物件,裡面便會包含 reference 到這些 attributes 所設定的數值,再來就可以選擇適當的 method 來取用了。
這邊有幾個需要注意的地方,由於 context.obtainStyledAttributes() 內部設計預設是認定 styleable 中的 attributes 是排序過的,所以他回傳回來的數值是依其在 native 的順序寫入到 TypedArray,若是我們的 styleable 沒 sort 過,就會得到非預期的結果;另外就是從 TypedArray 取值時傳入的是 attribute 在 styleable 的 index,而非 attribute 本身。
Reference:
http://droidux.com/programmatically-declare-styleable-custom-view/
然後在 FloatLabelLayout.java 取得外部利用這些 attributes 所設定的數值,進而調整裡面需要變動的元件。
但是有些時候,我們可能想要直接在程式碼中定義這些行為,那該如何實作呢?
在這個例子中,我們先宣告了 styleable 這個 int array,放入需要用到的 attributes,接著調用 Context#obtainStyledAttributes() 傳回一個 TypedArray 的物件,裡面便會包含 reference 到這些 attributes 所設定的數值,再來就可以選擇適當的 method 來取用了。
這邊有幾個需要注意的地方,由於 context.obtainStyledAttributes() 內部設計預設是認定 styleable 中的 attributes 是排序過的,所以他回傳回來的數值是依其在 native 的順序寫入到 TypedArray,若是我們的 styleable 沒 sort 過,就會得到非預期的結果;另外就是從 TypedArray 取值時傳入的是 attribute 在 styleable 的 index,而非 attribute 本身。
Reference:
http://droidux.com/programmatically-declare-styleable-custom-view/
2015/02/16
Avoid float and double if exact answers are required
float 和 double 主要是被設計用作科學及工程運算,若是需要精確結果的商業計算,它們並不適合,原因在於它們不能準確地表達 0.1 或任何 10⁻¹ 的倍數。
假設你口袋有 $1.03,而你花了 42¢,那會剩下多少?
不幸地,它的輸出結果是 0.6100000000000001,這不是唯一的特殊案例;假設你口袋有 $1,你買了 9 個 10¢ 的物品,那會找回多少零錢?
根據這個結果,你會拿回 $0.09999999999999998。
我們可能會覺得只要利用輸出前幾位數做四捨五入來 work around 就可以了,但這招並不是每次都能奏效。再來舉一個例子,假設現在你口袋有 $1,架上擺著糖果,價格依序為 10¢、20¢、30¢,依此類推,如果從第一顆糖果開始買起,你可以買多少顆糖果,並且能找回多少零錢?
當你跑完這段程式碼,你會發現你可以買 3 顆糖果,然後找回 $0.3999999999999999,這結果當然是錯的。要解決這個情況,我們必須改用 BigDecimal 來處理商業計算。
這次我們得到了正確的答案,你可以買 4 顆糖果,找回 $0.00。
不過,使用 BigDecimal 也有缺點,它寫起來不是很方便,而且格式也麻煩,不容易 review。另外一個方法就是視情況使用 int 或 long 來代換,像是在這個例子中,因為數值不大,我們可以把單位都改用分來計算。
Reference:
http://stackoverflow.com/questions/5257166
《Effective Java》Item 48
假設你口袋有 $1.03,而你花了 42¢,那會剩下多少?
不幸地,它的輸出結果是 0.6100000000000001,這不是唯一的特殊案例;假設你口袋有 $1,你買了 9 個 10¢ 的物品,那會找回多少零錢?
根據這個結果,你會拿回 $0.09999999999999998。
我們可能會覺得只要利用輸出前幾位數做四捨五入來 work around 就可以了,但這招並不是每次都能奏效。再來舉一個例子,假設現在你口袋有 $1,架上擺著糖果,價格依序為 10¢、20¢、30¢,依此類推,如果從第一顆糖果開始買起,你可以買多少顆糖果,並且能找回多少零錢?
當你跑完這段程式碼,你會發現你可以買 3 顆糖果,然後找回 $0.3999999999999999,這結果當然是錯的。要解決這個情況,我們必須改用 BigDecimal 來處理商業計算。
這次我們得到了正確的答案,你可以買 4 顆糖果,找回 $0.00。
不過,使用 BigDecimal 也有缺點,它寫起來不是很方便,而且格式也麻煩,不容易 review。另外一個方法就是視情況使用 int 或 long 來代換,像是在這個例子中,因為數值不大,我們可以把單位都改用分來計算。
Reference:
http://stackoverflow.com/questions/5257166
《Effective Java》Item 48
2015/01/21
Context, What Context?
我們在開發 Android app 時,最常用到的元件非 Context 莫屬了,但它也最有可能會被誤用。假設現在定義了一個 singleton,需要傳入 Context 來做後續如載入 resources 之類的工作,並且會將這個 reference 存下來。
這時會造成一個問題,我們不知道這個 Context 的來源是什麼,而如果來源是 Activity 或 Service,那我們扣著這個 reference 也是不安全的。原因在於為了維持這個 static reference,那與它相關聯的所有物件就不會被回收,如果這個 Context 來源是 Activity,這就意味著連同在這個 Activity 裡面的 View 以及各種物件都會被扣著,增加 memory leak 的風險。為了避免這個情況,我們可以調整 reference 到 Application Context 身上。
這樣我們便不用顧慮 Context 來源是什麼,因為 Application Context 本身就是 singleton,不需要再花資源建另外一個 static reference。
那我們為什麼不乾脆都只 reference 到 Application Context 身上就好了呢?那是因為有些功能不適合由 Application Context 執行,最主要的就是 UI 行為,例如 Layout Inflation,而執行 UI 功能最適合的人選就是 Activity,一般來說這些功能原本也就被設計在 Activity 內運作。
Reference:
http://possiblemobile.com/2013/06/context/
這時會造成一個問題,我們不知道這個 Context 的來源是什麼,而如果來源是 Activity 或 Service,那我們扣著這個 reference 也是不安全的。原因在於為了維持這個 static reference,那與它相關聯的所有物件就不會被回收,如果這個 Context 來源是 Activity,這就意味著連同在這個 Activity 裡面的 View 以及各種物件都會被扣著,增加 memory leak 的風險。為了避免這個情況,我們可以調整 reference 到 Application Context 身上。
這樣我們便不用顧慮 Context 來源是什麼,因為 Application Context 本身就是 singleton,不需要再花資源建另外一個 static reference。
那我們為什麼不乾脆都只 reference 到 Application Context 身上就好了呢?那是因為有些功能不適合由 Application Context 執行,最主要的就是 UI 行為,例如 Layout Inflation,而執行 UI 功能最適合的人選就是 Activity,一般來說這些功能原本也就被設計在 Activity 內運作。
Reference:
http://possiblemobile.com/2013/06/context/
2015/01/18
Android Performance Best Practices
Android Performance Best Practices from Amgad Muhammad
這幾天在看 Android Performance Patterns 相關文章時看到這份投影片,裡面提到的有些東西我以前沒用過,就來寫摘要記錄一下。
如何有效運用資源:
這幾天在看 Android Performance Patterns 相關文章時看到這份投影片,裡面提到的有些東西我以前沒用過,就來寫摘要記錄一下。
如何有效運用資源:
- 謹慎使用 service
- 利用 IntentService 可以確保任務執行完後會結束,藉此限制它的生命週期。
- 若 app 結束或不需要 service 資源時,務必釋放掉避免佔用。
- 當 UI 退場或是 memory 吃緊時,都可以利用 onTrimMemory() (API level 14 以上) 提供的 callback 得知是否需要釋放資源。
- 透過 getMemoryClass() 得知這台 device 可以用的 heap memory,超過便會造成 OOM;另外在 manifest<application> 中設定 largeHeap 為 true 的話,可以得到更多的 heap memory,此時若想得知可以用的 heap memory,需要改用 getLargeMemoryClass()。由於資源有限,若非必要,還是盡量避免使用 largeHeap。
- 小心引用 bitmap,並且確實回收避免資源浪費。
- 適當情況下可以利用 SparseArray 取代 Hashmap 改善資源運用。
- 注意 memory 基本用量
- 不要使用 enums,因為它會需要比 static 更多的資源。
- 每個 class 使用約 500 bytes。
- 每個 class 的 instance 約有 12~16 bytes。
- 每放一個項目到 Hashmap,便需要額外配置一個項目物件,佔用 32bytes。
- 小心使用抽象類別或方法,因為它同樣會需要 memory,如果它不能帶來明顯優勢,那應該避免使用。
- 小心使用第三方 library。
- 利用 ProGuard 精簡並混淆程式碼,減少資源需求。
Performance tips:
- 避免宣告無謂的物件,或是短暫使用的物件。
- 如果只需要調用物件內的方法,而不需要物件本身,可以設定方法為 static。
- 對於固定變數,使用 static final。
- 避免使用 getters 和 setters,因為同樣耗費調用成本,除非是在 public interface 裡使用;若是在 class,應該直接調用其變數。
- 使用 enhanced for-loop 語法會比傳統 counted loop 效率好非常多。
- 避免使用浮點數。
2015/01/11
Google Analytics SDK v4
最近將 Google Analytics 升級到 v4,碰到了一些改動及需要注意的地方。首先是 GA 已經內建在 Google Play Services SDK 中,我們只需要使用 Gradle 來 import 即可,原本在官網下載的 libGoogleAnalyticsServices.jar 就可以移除了。
在 GA v4 中,Google 依功能目的建立不同的 Tracker,並且建議統一放在 Application 來做管理。另外在 Application onCreate 時要記得做 Tracker 的初始化,不然會遺漏掉一些資訊,這是官方文件沒有提到的。
由於目前我們只需要收集自己 app 的數據,所以選擇 APP_TRACKER 來使用就夠了。接下來在 Application 提供一個 method 來讓其他元件取用。
GA v4 還新增了一個特色,就是可以讓 app 收集使用者的年齡、性別及興趣,只要像上述的程式碼一樣,利用 Tracker 呼叫 enableAdvertisingIdCollection 並啟用即可。
再來是原本我們都會在 res/values 底下放 analytics.xml 這個設定檔,現在則是改在 res/xml 底下定義 Tracker 各自的設定檔。如上所述,在這邊我們新增了一個 app_tracker.xml,
與之前的版本明顯不同的地方在於原本要在 report 中定義 screen name 的 tag,從 <string> 變更為 <screenName>。另外由於 Fragment 的生命週期特性,目前仍然不能在此定義它的 screen name,必須在 onStart 時主動傳送 screen view,
而傳送 event 則是改用 EventBuilder 來建立。
這次還多了幾個好用的進階功能,例如我們若想避免在 test 或 debug 時的行為也被 GA 記錄到 report 中,可以使用 dryRun 這個 flag 來設定。
若是想調整 GA 的 log 層級,也可以利用 Logger 來設定,預設是輸出 warning 和 error。
Reference:
https://developers.google.com/analytics/devguides/collection/android/v4/
https://support.google.com/analytics/answer/2799357
http://stackoverflow.com/questions/22821139
在 GA v4 中,Google 依功能目的建立不同的 Tracker,並且建議統一放在 Application 來做管理。另外在 Application onCreate 時要記得做 Tracker 的初始化,不然會遺漏掉一些資訊,這是官方文件沒有提到的。
由於目前我們只需要收集自己 app 的數據,所以選擇 APP_TRACKER 來使用就夠了。接下來在 Application 提供一個 method 來讓其他元件取用。
GA v4 還新增了一個特色,就是可以讓 app 收集使用者的年齡、性別及興趣,只要像上述的程式碼一樣,利用 Tracker 呼叫 enableAdvertisingIdCollection 並啟用即可。
再來是原本我們都會在 res/values 底下放 analytics.xml 這個設定檔,現在則是改在 res/xml 底下定義 Tracker 各自的設定檔。如上所述,在這邊我們新增了一個 app_tracker.xml,
與之前的版本明顯不同的地方在於原本要在 report 中定義 screen name 的 tag,從 <string> 變更為 <screenName>。另外由於 Fragment 的生命週期特性,目前仍然不能在此定義它的 screen name,必須在 onStart 時主動傳送 screen view,
而傳送 event 則是改用 EventBuilder 來建立。
這次還多了幾個好用的進階功能,例如我們若想避免在 test 或 debug 時的行為也被 GA 記錄到 report 中,可以使用 dryRun 這個 flag 來設定。
若是想調整 GA 的 log 層級,也可以利用 Logger 來設定,預設是輸出 warning 和 error。
Reference:
https://developers.google.com/analytics/devguides/collection/android/v4/
https://support.google.com/analytics/answer/2799357
http://stackoverflow.com/questions/22821139
訂閱:
文章 (Atom)