Kotlin/NativeでAndroidのNativeメソッドを実装してみた
この記事はAndroid Advent Calendar 2018の5日目21日目の記事です。
概要
普段C++/Cで実装しているAndroidのNativeメソッドをKotlin/Nativeを使って実装できないか試してみたというお話です。
サンプル
GitHubにリポジトリを上げました。 Android Studio 3.2.1上でビルドできるのを確認しました。
プロジェクトの構成は
app
: Androidプロジェクトgreeting
: Kotlin MultiPlatformプロジェクト。ここでJNI関数を実装
です。
Dynamic Libraryのビルド
ドキュメントを参考に設定できました。
fromPreset
関数の第一引数にpresets.androidNativeArm32
(64bit版をビルドしたい場合はpresets.androidNativeArm64
)を設定し、
ブロックの中でcompilations.main.outputKinds
をdynamic
に指定するとAndroid向けのsoファイルが生成できます。
plugins { id 'kotlin-multiplatform' } kotlin { targets { fromPreset(presets.androidNativeArm32, 'arm32') { compilations.main.outputKinds('dynamic') } } // (省略) }
JNI関数の定義
Kotlinの関数をトップレベルのC関数として定義するには以下のように@CName
アノテーションを利用します。
また、関数のシグネチャーは後述するPlatform Libraryの定義を参考にしました。
ちなみにJNIEnvVar
, jobject
, jstring
などはすべてPlatform Library(platform.android
パッケージ)に定義されています。
@CName("Java_io_github_tsuyosh_kotlinnativejni_Greeting_say") fun Java_io_github_tsuyosh_kotlinnativejni_Greeting_say( env: CPointer<JNIEnvVar>?, obj: jobject?, name: jstring? ): jstring? { // (省略) }
型の相互変換
INREROP.mdを読んで雰囲気で理解しただけですがこんな感じで相互変換しました。
jstring
とString
Kotlin/Nativeのサンプルコード等を見ながら変換用のユーティリティークラスを作りました。
(追記) 実はJNIのなんとかUTF関数が扱う文字コードはmodified UTF-8であることが発覚したので修正する予定ですorz
JNIのUTF-8って独自仕様だったの今頃知った時の顔をしている... https://t.co/qkZuGfh7Ku
— tsuyoshi uehara (@uecchi) December 27, 2018
jboolean
とBoolean
typealias jboolean = UByte
と定義されています。trueが1.toUByte()
でfalseが0.toUByte()
になります。
jint
とInt
typealias jint = Int
と定義されていますので特に変換処理はいらないです。
感想
C言語由来の型を扱うのに慣れるまで時間がかかる
INTEROP.mdを読むのが辛かった... この辺はいずれまとめたいと思います(忘れてなければ)
Platform Libraryのドキュメント少ない
ドキュメントを見ても概要しか書いていないのでどんなAPIがあるのか調べるのに苦労しました。。。
わかったことをまとめるとPlatform Libraryの定義が知りたい場合はkotlin-nativeの配布パッケージのklib/platform/<target>
以下を見て、
$ <KOTLIN_NATIVE_DIR>/bin/klib contents <KOTLIN_NATIVE_DIR>/klib/platform/android_arm32/android
の様に確認するのが手っ取り早いと思います。
(Platform Libraryはよく使われるライブラリをcinterop
を使わなくても扱えるようにしただけという感じなのかも)
まだIDEの補完機能が使えないクラスがあるのが辛い
kotlinx.cinterop
やplatform
以下のパッケージがまだコード補完できないし、型の定義を見ることもできません。
ひょっとしてCLionを使えばいいのかもしれないけどAndroid Studioでも対応して欲しい。。
Kotlin/Nativeのターゲットにandroid-x86がない
Androidエンジニアが普段使っているだろうx86ベースのエミュレーターでは確認できないので実機が必須です。 (armベースのエミュレーターは実用的じゃないので除外) android x86もターゲットに加えてほしい🙏
現状ではC++言語のライブラリが利用できない
公式サイトを見るとC++のライブラリを利用できない状態です。 JNIの組み込み関数もC言語方式の書き方で利用しないといけません。
On the other hand, Kotlin/Native supports interoperability to use existing libraries directly from Kotlin/Native: - static or dynamic C Libraries - C, Swift, and Objective-C frameworks
今後の展望
今回のサンプルはJava/Kotlinでも実装できる簡単なものでしたがcinterop
ツールを使えばC言語のライブラリを使うこともできると思います。
機会があればその辺も触って感想を書いてみたいと思います。
また、公開するシンボルをJNI関連に絞れなかったのでビルドオプションを直したいです。