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/

2015/01/18

Android Performance Best Practices


Android Performance Best Practices from Amgad Muhammad

這幾天在看 Android Performance Patterns 相關文章時看到這份投影片,裡面提到的有些東西我以前沒用過,就來寫摘要記錄一下。

如何有效運用資源:
  1. 謹慎使用 service
    • 利用 IntentService 可以確保任務執行完後會結束,藉此限制它的生命週期。
    • 若 app 結束或不需要 service 資源時,務必釋放掉避免佔用。
  2. 當 UI 退場或是 memory 吃緊時,都可以利用 onTrimMemory() (API level 14 以上) 提供的 callback 得知是否需要釋放資源。
  3. 透過 getMemoryClass() 得知這台 device 可以用的 heap memory,超過便會造成 OOM;另外在 manifest<application> 中設定 largeHeap 為 true 的話,可以得到更多的 heap memory,此時若想得知可以用的 heap memory,需要改用 getLargeMemoryClass()。由於資源有限,若非必要,還是盡量避免使用 largeHeap。
  4. 小心引用 bitmap,並且確實回收避免資源浪費。
  5. 適當情況下可以利用 SparseArray 取代 Hashmap 改善資源運用。
  6. 注意 memory 基本用量
    • 不要使用 enums,因為它會需要比 static 更多的資源。
    • 每個 class 使用約 500 bytes。
    • 每個 class 的 instance 約有 12~16 bytes。
    • 每放一個項目到 Hashmap,便需要額外配置一個項目物件,佔用 32bytes。
  7. 小心使用抽象類別或方法,因為它同樣會需要 memory,如果它不能帶來明顯優勢,那應該避免使用。
  8. 小心使用第三方 library。
  9. 利用 ProGuard 精簡並混淆程式碼,減少資源需求。
Performance tips:
  1. 避免宣告無謂的物件,或是短暫使用的物件。
  2. 如果只需要調用物件內的方法,而不需要物件本身,可以設定方法為 static。
  3. 對於固定變數,使用 static final。
  4. 避免使用 getters 和 setters,因為同樣耗費調用成本,除非是在 public interface 裡使用;若是在 class,應該直接調用其變數。
  5. 使用 enhanced for-loop 語法會比傳統 counted loop 效率好非常多。
  6. 避免使用浮點數。

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