Subscribed unsubscribe Subscribe Subscribe

Islands in the byte stream

Technical notes by a software engineer

AndroidでJava8は使えるのか?

まず「Java8」というとき、それがどういう意味なのか考えないといけません。

「Java8」のコンポーネントには主に次の三種類があると思います。

  • lambdaを代表としたJava8の構文
  • streamやoptionalなどのJava8の標準ライブラリ
  • 最新のJVMの仕様(特にGC)

最後のJVMの仕様はAndroidとは関係ないとしても、構文と標準ライブラリは別々に考えないといけません。

Java8の構文

Java8の構文はretrolambdaを使うことでAndroidでもある程度使えます。このとき、Android OSのバージョンは関係ありません。

orfjackal/retrolambda · GitHub

retrolambdaはJVM6用にJava8の言語機能をバックポートするためのライブラリでlambdaという名前が入っているものの、lambdaだけではなく他にも幾つかの機能をバックポートしています。

READMEから抜粋すると:

  • Lambda expressions
    • anonymous inner classに変換して実現
  • Method references
    • もともと単なるlambda式の糖衣構文なので、lambda式と同じ方法で実現可能
  • Try-with-resources statements
    • Throwable.addSuppressedの呼び出しを削除する
  • Default methods
    • interfaceのデフォルトメソッドを持つためのクラスを用意して、インターフェイスを実装したときにそれを呼び出すメソッドを追加する
  • Static methods on interfaces
    • interfaceのスタティックメソッドを持つためのクラスを用意して、必要な時にそれを呼び出すようにする

ただし、Java8の標準ライブラリは使えないのでうっかり使わないように注意が必要です。

Java8の標準ライブラリ

現在のところ、新しい標準ライブラリをAndroidで使えるようになるにはAndroid OSのアップデートが必要だと考えられています。つまり、Android NでJava8に完全対応したとして、それを実際に使えるようになるのはminSdkVersionをN以降にしたときだけです。

ただ、古いOSでJava8(あるいはJava9, 10)を使う望みが全くないわけでもないと思っています。つまり、Java8の標準ライブラリを使っているアプリケーションをビルドするときに、APKに標準ライブラリをバンドルすれば実現可能なのではないかと考えています。

もちろんその際、多少のバイトコード操作が必要です。たとえばArrayListを受け取るAndroid FrameworkのAPIにバンドル版のArrayListを渡すことはできないので、Java6などにも存在するクラスについてはそちらを使うようにしつつ、 Collection#stream() などの新しいメソッドはstatic methodに置き換えるなどすることになるでしょう。しかし標準ライブラリのバンドルは技術的には可能ではないかと思います。

ただし、素朴に大量のクラスライブラリをバンドルすると、それだけでAPKのサイズが非常に大きくなる(ことによると+50MB以上)ので、これを削らないと実用に耐えないと思われます。

ここで実際、Xamarin.AndroidはC#の標準ライブラリランタイム(mscorlib)をバンドルしますが、リンク時に未使用のコードを削除することで大幅に削減できるとしています。

Application Package Sizes - Xamarin

When we build an application for distribution, we execute a process, known as Linking, that examines the application and removes any code that is not directly used. This process is similar to the functionality that Garbage Collection provides for heap-allocated memory.

そしてAndroidでもすでにProGuardで未使用コードの削除ができます。したがって、本当にすべてをバンドルするわけではありません。とはいえ必要な分だけバンドルするだけでもかなり大きくなる可能性はありますし、APKサイズとビルド時間の増加は避けられないと思われます。

そしてすべての技術的課題を解決してJava8の標準ライブラリが使用可能になったとして、古いAndroid Frameworkは依然としてJava8に対応しません。つまりOptionalを返してほしいところでもnullを返します。そして後方互換性のために、nullを返していたメソッドをあるバージョンからOptionalを返すようにはしないでしょう。つまりAndroid FrameworkのJava8対応を古いAndroidにバックポートするのはあまりにも困難で、それゆえにJava8標準ライブラリのバックポートも価値は半減します。

そういうわけで、標準ライブラリに関してのぼくの予想は次のとおりです。

  • 新しいAndroidではJava8は使えるようになるだろう(Nかそれ以降かはともかく)
  • Java8の標準ライブラリをAndroid 4などで使えるようにするのは、Java8の標準ライブラリをバンドルすることで技術的には可能である
  • しかしAPKサイズやビルド時間などとトレードオフとAndroid FrameworkのJava8対応は現実的ではないことから、標準ライブラリのバックポートプロジェクトは公式には行われない可能性が高い