A harddisk
A harddisk

上月 kotlinx.serialization 出了 1.0 版。除了支援 JSON 之外,還有支援 Protocol Buffers (Protobuf),而且還是跨平台支援。而在前一個月 Android 出了 Jetpack DataStore,它是一個用來取代 SharedPreferences 的 library。它有兩種用法:

  1. Proto DataStore:用 Protobuf 來儲存資料,存取時候都是直接經 Java/Kotlin class,所 …


Conventional Commits 是一個簡單的 Git commit message 約定,用來規定 commit message 的寫法。Git 本身就沒有規定 commit message 的內容格式,所以不同人會有不同的做法。如果 repository 只是有一個或幾個人用的話,那問題就不大。但如果在開源軟件或者公司這類多人同時參與的 repository 時,不同風格的 commit message 會令人花額外的時間來了解 repository 的變動。如果再加上一部分的 commit message 內容空洞的時候(例如只寫「fix」、「commit」、「修正錯誤」等等),情況就會失控。

一般的 Git client 因為界面空間和方便檢索,在顯示紀錄時都是顯示 com …


在日常生活中,我們都會用到不同的量度單位。例如重量有時會用公斤 (kg),有時會用磅 (lb),有時又會用斤之類。如果在 Java 上表示這些數值,用 intfloatdouble 的話有時會令人理解錯誤,就好像 UNIX timestamp 般一時會用秒一時會用毫秒。為防止人們誤解這個 variable 或 method 的時間單位,通常都要在名稱加上 millis 之類的後綴。知名例子有 System.currentTimeMillis 。如果處理時間的話,現成的有 。不過如果去到之前的重量單位的話,JDK 就沒有現成的 class。

如果是 Android 的話可以用 android.icu.util.Measure,但要 API level 24 (Android 7.0) …


如果有做過 Android 開發的話應該都有用過 annotation processor(又稱 codegen),即是在 build.gradle 入面要用 annotationProcessor 或者 kapt 的那些 dependency。用法大概是在 code 上加上一些 @ 開頭的 annotation,然後 build 出來就會自動幫你生成相關的 class。簡單來說 annotation processor 就是用 code 來讓 Java compiler 生成 code。通常都是用來生成一些內容重覆的 code 來代替自己人手寫。

自己做一個功能不多的 annotation processor 其實都不太難。難的地方是 debug 時不能像平常般加 breakpoint,加上 …


Moshi 是一個 JSON serialization/deserialization 的 library。和 Gson 不同的是它提供了 Kotlin Codegen,它可以生成 serialization/deserialization 的 adapter class,所以可以避免使用 reflection,而且 adapter 還會參照 Kotlin 的 non-null 和 default value。不過最近發現 production app 會出現 parameter type is null 的錯誤訊息。

parameter type is null
parameter type is null
parameter type is null
java.lang.NoSuchMethodException: parameter type is null at java.lang.Class.getConstructor0(Class.java:2322) at java.lang.Class.getDeclaredConstructor(Class.java:2166) at net.swiftzer.metroride.remote.mtrupdate.status.LineStatusResponse_LineStatusJsonAdapter.a(LineStatusResponse_LineStatusJsonAdapter.kt:70) at net.swiftzer.metroride.remote.mtrupdate.status.LineStatusResponse_LineStatusJsonAdapter.a(LineStatusResponse_LineStatusJsonAdapter.kt:19) at f.d.a.t.a.a(NullSafeJsonAdapter.java:40) at f.d.a.d.a(CollectionJsonAdapter.java:76) at f.d.a.d$b.a(CollectionJsonAdapter.java:53) at f.d.a.t.a.a(NullSafeJsonAdapter.java:40) at net.swiftzer.metroride.remote.mtrupdate.status.LineStatusResponseJsonAdapter.a(LineStatusResponseJsonAdapter.kt:47) at net.swiftzer.metroride.remote.mtrupdate.status.LineStatusResponseJsonAdapter.a(LineStatusResponseJsonAdapter.kt:21) at f.d.a.t.a.a(NullSafeJsonAdapter.java:40) at q.z.a.c.a(MoshiResponseBodyConverter.java:45) at q.z.a.c.a(MoshiResponseBodyConverter.java:27) at q.m.a(OkHttpCall.java:225) at q.m.h(OkHttpCall.java:188) at q.y.a.c.b(CallExecuteObservable.java:45) at i.a.e.a(Observable.java:12268) at q.y.a.a.b(BodyObservable.java:34) at i.a.e.a(Observable.java:12268) at i.a.p.e.b.d.b(ObservableSingleSingle.java:35) at i.a.i.a(Single.java:3603) at i.a.p.e.c.b.b(SingleMap.java:34) at i.a.i.a(Single.java:3603) at i.a.p.e.c.e.b(SingleZipArray.java:63) at i.a.i.a(Single.java:3603) at i.a.p.e.c.b.b(SingleMap.java:34) at i.a.i.a(Single.java:3603) at i.a.p.e.c.d$a.run(SingleSubscribeOn.java:89) at i.a.h$a.run(Scheduler.java:578) at i.a.p.f.h.run(ScheduledRunnable.java:66) at i.a.p.f.h.call(ScheduledRunnable.java:57) at java.util.concurrent.FutureTask.run(FutureTask.java:266)…


最近工作需要做 Firebase Cloud Messaging (FCM) 整合,發現了向 Firebase API 直接送出 push 的 HTTP request 都可以生成不同種類的 message。

如果要整合到 Android 的話,需要建立一個新的 Service class 並繼承自 FirebaseMessagingService。這個 Service 有一個叫 onMessageReceived 的 callback method 來接收來自 FCM 的 push 和它的 payload。但原來不是所有的 push 都能被那個 callback 接到,要視乎 push 的種類和你的 app 當時在甚麼情況而定。

FCM 的 push 有分兩種:Notification me …


一個 app 的第一印象應該是它的 app icon (launcher icon)。在 Android,在不同的時期前後出了好幾個 app icon 規格。但是 Android 介紹不同種類的 app icon 的文件放得非常分散,如果平時沒有一直留意的話都幾乎肯定會做錯或者做漏。以下是全部 Android app 都會用到 app icon:

  • Launcher icon(圓形)
  • Adaptive launcher icon
  • Google Play icon

如果想簡單地做出全部 icon 的話,可以用 Android Studio 內置的 Image Asset Studio。但如果想追求完美的話,還是自行準備圖片比較好。這篇文章整理了不同 icon 的基本規格 …


上一篇為大家介紹了如何用 Google Apps Script 建立 Google Calendar event。這一次就示範用 Google Apps Script 發送電郵(即是 mail merge)。

準備內容

上次我們用 2019 年香港公眾假期作例子,這次我們用得獎名單做例子。

Image for post
Image for post

第二及第三欄是我們會在電郵內文用到;Sent By 和 Sent At 兩欄是用來判別這封電郵是否發送過。如果已經發送過就會標明是由誰人執行和執行時間。

準備內文範本

和之前一樣,都是用 Google Apps Script 的 HTML template 功能。

<p>Dear <?= name ?>,<p><p>Congratulation! You won our smartphone giveaway. Your <? …

Eric Li

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store