21 October 2014

最近兩天花了一點時間在 Android 專案裡導入 retrolambda,真是一試成主顧。它的原理是改寫 lambda compile 出來的 bytecode,把它轉成 Java7 能看的懂的 anonymous class。這樣就能在 Android 裡自由寫 lambda 啦:

//listener 使用 lambda expression
view.setOnClickHandler( v -> System.out.println("hello"));

//method reference 也通
runOnUiThread(this::finish);

由於是 compile 過程中偷偷轉的,產生出的也是標準的 bytecode,相容性相當的好,也不需要加裝額外的 library。而且未來 Android 正式支援 Java8 後,程式都不用動,只要把 retrolambda 移除再重新 compile 就好,完全是無痛的升級。

在 Android 裡要使用很簡單,已經有人寫好 gradle plugin,設個兩三行就搞定了。實際測試 Android 2.3~5.0 版都 ok,Java 8u5~8u25 也都相容,非常滿意啊。

gradle plugin 有一點沒提到,在 proguard 裡要加個特別設定才會過關:

# java.lang.invoke.* 是 java 8 的 class
# 這是 plugin 加入的暗椿,要忽略掉
-dontwarn java.lang.invoke.**

Android 非常需要 lambda expression

retrolambda 只支援 lambda expression,而 Java8 另一個大改進是 Stream library,讓你可以用 functional 風格來撰寫程式,簡化 collection 的操作,不過這些在 Android 還是不能使用。但即使只有 lambda 幫助還是很大,因為 GUI client 有太多 被動 的事件要處理,小至 click listener,大至非同步操作,在沒有 lambda 前通通都是堆滿滿的 anonymous class, 還有那噁心的 nested callback hell,非常的痛苦。

尤其是專案開始走向 PromiseReactive 這種風格開發時,lambda 是完全必要的工具,不然程式碼可讀性實在是太差。在沒有 retrolambda 之前,你還有個理由不採用這種風格開發,但現在阻力都沒了,你該開始在 Android 裡擁抱 Promise pattern 來處理非同步的工作。

常見的 Android async library 有:

  • Bolts (facebook)
  • Jdeferred
  • RxJava (netflix, reactive)
  • 其實還有三、四種變型,不過我一時找不到

RxJava 是比較大型的 library,它採用 reactive programming,比其他單純的 promise library 做的更多。RxJava 詳細以後再談,我目前的心得是它很多概念都是對的,但實作上我覺得太過複雜,它是個兩面刃,要導入請小心。我想在 Android 上還是選擇輕量的 library 比較合適。

我們團隊目前沒有採用上述的 open source 工具,不過已經採用 promise pattern 一段時間了 (自行開發的迷你 library, 歷史因素…)。retrolambda 的導入瞬間就移除掉一堆 anonymous class ,程式碼超乾淨,這豈是個爽字可以形容。

開發 Android 使用 lambda expression,真他媽的爽

retrolambda 的缺點是,如果未來 java 8 改了 lambda 的實作就不能用了。到時會有轉換陣痛期,但我相信短期內不會有太大的變動,等到有變動時應該已經是很久之後了,Google 那時也該正式支援 Java 8 了吧。