Xamarin.AndroidのN previewとJava8サポートについて

Google I/Oが近づいていますが、それに先立ってAndroid N previewが公開されていますね。既にpreview 2で、今後も5週間ごとにプレビューが出続けるというので、最新版Androidへの対応もだいぶやりやすくなったなという感じです。

そんなわけで、Xamarin.AndroidのMono.Android.dllも、OSS版xamarin-androidとXamarin.Androidの最新preview版は、最新のN preview 2まで対応しています。

Android N APIの最大の特徴は、Java8に対応したことですが、Xamarinユーザーにとっての恩恵はほぼ無いでしょう。

恩恵は無いのですが、Java8サポートは極めて難しい問題をもたらしました。インターフェースのデフォルトメソッドです。当初、N preview 1が登場した後、Mono.Android.dllはこれらについてもメソッドを生成していました。しかし、C#Javaとは異なり、デフォルトメソッドをサポートしません。当然ながら、インターフェースで定義されたメソッドは実装しなければならないことになります。Javaではこれは「やってもいいし、基本やらなくていい」ことなのに、C#になったとたん「やらなければならない」ことになるのは大変です。Java.Util.Iteratorなど、ごく基本的なインターフェースに、突如大量のメソッドが追加されて実装しなければならなくなったわけです。しかしインターフェースでAPIを定義しないと、誰も実装をオーバーライドできないことになります。

しばらく考えましたが、これはいくら何でもメソッドを追加するわけにはいかない、ということで、今のところ、デフォルトインターフェースメソッドは生成しないことにしました。通常のインターフェースメソッドでこういう「省略」を行うと、アプリケーション開発者がバインディングクラスの派生クラスを定義した時に生成するAndroid Callable Wrapper (ACW)が必要なインターフェースメソッドを生成せず、javacがコンパイルエラーを起こすことになるわけですが、インターフェースデフォルトメソッドの場合には問題になりません。

必要であれば、多少ややこしいコードを書く必要がありますが、手作業で追加することも一応可能です。

この辺は、OSS版xamarin-androidのrepoでRFC状態になっているので、何かしら意見があれば書いておいて下さい。

Java 8 Interface Binding · Issue #25 · xamarin/java.interop · GitHub

個人的には、まだどこにどう需要が出てくるかわからない現状で、中途半端なバインディング生成機能を実現したくない派です。現状ほぼメリットないし。

ちなみに、Android SDKでJava8が必要となるソースをコンパイルする際には、javacではなくjackという新しいGoogleコンパイラーを使用する必要がある(ということになっている)ことになっているのですが、

gfx.hatenablog.com

Xamarin.Androidが内部的に生成するコードはJava8の文法を使用しませんし、Java8のjavacをそのまま使っていても問題ないはずです。

JavaバイトコードをDalvikに変換するdx(dx.jar)には、--no-strictというオプションがあって、これを指定しないとJava8のバイトコードは拒絶されてしまうのですが、Xamarin.Androidでは内部的にこれを指定してdxを呼び出しています。

また、java8が問題になるのはproguard(およびそれを内部的に使用するmultidexのツールmainDexClasses)で、この古いツールはJava8に対応していないので、Xamarin.Androidではproguardの使用が指定されていたら、Java7にフォールバックします(実のところ、proguardの中身をいじって、参照されているデフォルトメソッドも全部スキャンするようにしてしまえば良いわけで、それほど対応が難しいとは思えないのですが、Googleはjackへの移行を促進するために政治的に非対応にしているんじゃないかと邪推しています)。

以上、インパクトがありそうでない、ちょっとしたXamarin.Android小ネタでした。