Open13

Rustでアンドロイドアプリ開発をしたい

白山風露白山風露

ここからRust ネイティブオンリーで開発するなら多分これだけでOK
以下はJVMなコードを含める場合

白山風露白山風露
  • cargo-apkはネイティブオンリーでの開発を想定されているのでJVM言語をビルドする仕組みにはなっていない
  • Java関数を呼び出すだけならJNIでできる
  • 普通にAndroid Studioでプロジェクト作ってネイティブライブラリをビルドする仕組みにするのが一般的だと思う
  • でもせっかくだから全部Rustのエコシステムに乗せたい
  • cargo-mobileというものがあるらしい
白山風露白山風露
  • ……作るか
  • 作った
  • cargo-mobileはAndroidプロジェクトを自動生成してくれる
  • 思想としては全部cargo-mobileが面倒見るから自動生成に任せるべき的な感じらしく生成したAndroidプロジェクトはまるごと.gitignoreに入れられている
  • が、そこまでうまく行ってない感じなので結局Gradle スクリプト色々弄る
  • まあまだcrates.ioにもリリースされとらんしこれからの発展に期待しよう
白山風露白山風露
白山風露白山風露
  • とりあえずAndroidManifest.xmlをいじってandroid:hasCode="true"にする
  • Kotlinを使ってみようと思ったのでcargo android openでAndroid Studioを立ち上げ(別にこの方法じゃなくても良いんだけど)、メニューのTools -> Kotlin -> Configure Kotlin in ProjectでKotlinの設定を追加
    • したらビルドスクリプトがbuild.gradle.ktsなのにgroovy用のコードが追加されてエラー出まくったので修正
  • appのbuild.gradle.kts
    android {
        sourceSets.getByName("main") {
            // ...
            kotlin.srcDir("src/main/kotlin")
        }
    }
    
    を追加。これでこの下の*.ktファイルがコンパイルされるはず
  • 適当にcom.example.my_app.Util的なクラスを追加してstaticメソッドを使えばいいかな?と思ってやってみる
  • なんかJNIからクラス探索が失敗する
  • わからん
  • わからんので方法を変えた。調べてみたところNativeActivityはサブクラス化できるのでサブクラス化してそいつにメソッドを生やす。Activityはネイティブコード側から必ず取れる(取れなかったらウィンドウの初期化も何もできない)のでそこに生えたメソッドなら確実に呼べる
  • com.example.my_app.MyActivity的なクラスを生やす
  • AndroidManifest.xmlactivity要素をandroid:name="com.example.my_app.MyActivity"にする
  • あとついでにandroid:theme="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen"にもする
  • これでフルスクリーンかつナビゲーションバーのないアプリになった
白山風露白山風露
  • これだけなら正直言うとRustオンリーでも良かった気もする。DeprecatedとはいえsetSystemUiVisibilityで用は足りる訳だし
  • とはいえ、今はまだ単純に画面表示しているだけだけど、そのうちまたマネージドコードが必要になることも無いとは言えないし、その時になってビルド方法を大きく変えるのは面倒そう
  • なのでこれで良しとしておこう
白山風露白山風露
  • ところで改めてsetSystemUiVikibilityの方で試してみたところ例外が発生して落ちた
  • どうもJNIを使っていた時にうまくいっていたように見えたのは例外を握りつぶしていたかららしい
  • どんな例外が発生しているかと調べたところ "android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views."
  • どうやら初回のEvent::Resumeと二回目以降(Activityを移って戻ってきた時など)のEvent::Resumeで呼び出しスレッドが違う(そんなことあるのか……)
  • 結局runOnUiThreadからは逃れられない
白山風露白山風露
  • もうすこし調査したところRustのネイティブスレッドは常に同じだったがJVMスレッドが毎回違う(理由はよく分からない。AttachCurrentThreadの度に異なるJVMスレッドと認識される?)
  • JVMスレッドが異なるならメインスレッドでもUIスレッドでもないのでsetSystemUiVisibilityは常に失敗しそうなものだが成功する理由もよく分からない
  • まあ結局のところ常にrunOnUiThreadを使っておけば良さそう。
白山風露白山風露
  • JVMスレッドの問題はやはりAttachCurrentThread(実際にはjni::JavaVM::attach_current_thread)を使っていたことが原因だったようで、代わりにGetEnvを使う仕組みに切り替えたところ問題は解消
  • はじめにクラスが見つからなかったのもここが原因の可能性が高いと思う
    • Activityをサブクラス化する今のやり方のほうがスマートに思えるのでもとに戻す気はない