Android Gradle Plugin 4.0でjava.timeがバックポートされるようになりました

Java 8 library desugaring in D8 and R8で、desugarがA subset of java.timeに対応したとのことで、ThreeTenABPが置き換えられるのでは?と思い、ウキウキで試してみました。

環境は com.android.tools.build:gradle:4.0.0-alpha01になります。

セットアップ

build.gradleに、coreLibraryDesugaringEnabledを追加します。

compileOptions {
  sourceCompatibility JavaVersion.VERSION_1_8
  targetCompatibility JavaVersion.VERSION_1_8

  coreLibraryDesugaringEnabled true
}

これで完了です。

java.timeのAPIを呼び出してみる

29, 21のエミュレーターで次のコードを試したところ、クラッシュすることなく、無事に実行することが出来ました!

// Instant API
val date = Date()
val instant = date.toInstant()
binding.instant.text = instant.epochSecond.toString()

// ZoneId API
val zoneId = ZoneId.systemDefault()
binding.zoneId.text = zoneId.id
println(instant.atZone(zoneId).dayOfMonth)
println(instant.atZone(zoneId).month)

// LocalDate API
val now = LocalDate.now()
binding.localDate.text = now.dayOfMonth.toString()
println(now.dayOfMonth)

// ZoneOffset API
val zoneOffset = ZoneOffset.ofHours(10)
println(zoneOffset)

どのようにして実現しているか?

apkの中身を見たところ、java.time用のバックポートライブラリを準備しておいて、それをapkの中に組み込んでいるようでした。

以下、apkの中身になります。

java.timej$.timeに変換されていました。

また、Date#toInstantなど、既存クラスに新しく追加されたdate.timeのAPIも使えます。R8のコードを読んだところ、おそらく次のAPIが対応しています。

"retarget_lib_member": {
  "java.util.Calendar#toInstant": "java.util.DesugarCalendar",
  "java.util.Date#from": "java.util.DesugarDate",
  "java.util.Date#toInstant": "java.util.DesugarDate",
  "java.util.GregorianCalendar#from": "java.util.DesugarGregorianCalendar",
  "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar"
},

このあたりのAPIが使えるのは便利そうです。

ThreeTenABPへの置き換え

バックポートされたj$.timeのクラスと、ThreeTenABPのクラスを比較したところ、ほとんどのクラスが同じだったので、パッケージパスを変えるだけで、問題なく置き換えが出来ると思います。 ただもちろん、パフォーマンス、バグなどの問題はある可能性があります。

まとめ

今回の検証に用いたサンプルコードは DesugarTimeFragment.kt にあります:D

Written by
あんどろいどでぃべろっぱぁー🍎