たくさんの自由帳
Androidのお話
たくさんの自由帳
投稿日 : | 0 日前
文字数(だいたい) : 5924
目次
本題
こうしき
対応する必要があるか確認
はじめに
AGP をバージョンアップ
対応パターン
使ってるライブラリがネイティブコードを使ってる場合
AndroidX の .so ファイルのみが表示されている場合
証拠は
俺がライブラリ作者だ / ネイティブコードをビルドしている場合
すでに 16KB ページサイズに対応しているか確認
対応方法
環境
最短ルート
Android Studio で有効にする
Android Studio を使わない他の C++ プロジェクトで CMakeLists.txt に指定する
Rust などのクロスコンパイルで Android 向けビルドを 16KB ページサイズに対応させる
16KB ページサイズの動作確認
16KB ページサイズでビルドしたら古い端末で動くの?
どうもこんばんわ。
前回貼り付け忘れた画像です。かわいい
Android と Rust - たくさんの自由帳
https://takusan.negitoro.dev/posts/android_rust/
前回NDK
を使うアプリを作りました。そうです、各 CPU アーキテクチャ
向けにビルドするあれです。NDK
を使う(使った)アプリを開発する際、この16KB ページサイズ
に対応する必要があります。
Compatibilidad con tamaños de página de 16 KB | Compatibility | Android Developers
https://developer.android.com/guide/practices/page-sizes?hl=es-419
Get your apps ready for 16 KB page size devices
Android is adopting a 16 KB memory page size to improve performance. Use this guide to ensure your app is compatible with the new page size.
https://android-developers.googleblog.com/2024/12/get-your-apps-ready-for-16-kb-page-size-devices.html
Adding 16 KB Page Size to Android
Android 15 has been refactored to support using 16 KB page sizes, and file systems like EROFS and F2FS have been made compatible.
https://android-developers.googleblog.com/2024/08/adding-16-kb-page-size-to-android.html
リリース APK
を作った後に、Analyze APK ...
を押すことで、APK
の中を見れます。
で、こんな感じに、lib
フォルダがあれば対応が必要です。
が、今のアプリ開発だとlibandroidx.graphics.path.so
が入ってしまう?
この記事で言うネイティブコードは、クロスプラットフォームの人たちが言う 各プラットフォームの開発言語 (Swift / Kotlin) の事では無く、C++
やRust
のような Android NDK が必要なコードのことを指します。
ネイティブライブラリに関しても同じ。
AGP
バージョン8.5.1
以上にすると共有ライブラリのパッケージを更新するのセクションは完了しそう。
ネイティブコードを使ってる場合は、自分で対応するにしろ、対応してもらうにしろ何にしろAGP
のアップデートが必要そう?(よくわからない)
私の環境のAndroid Gradle Plugin
は8.8.2
。Android Studio
にはAGP Upgrade Assistant
がついているので古い場合は上げちゃえば良さそう。
多分このよっつ?
AndroidX
のlibandroidx.graphics.path.so
、libdatastore_shared_counter.so
だけ入ってる場合
androidx.
から始まる(Android Jetpack
)のライブラリが.so
ファイルに依存しているAndroid Studio
でCMakeLists.txt
とかを書いてC++
をコンパイルしている場合Android
へ持ち込んでいる場合ライブラリ作者にお願いしてきてください
よろしくお願いします
libandroidx.graphics.path.so
やlibdatastore_shared_counter.so
とかが表示されている場合。androidx.
のライブラリを入れたら付いてきた場合。
この場合は、すでに 16KB ページサイズでライブラリを配布しているので問題ない(はず)です。観測してる範囲では。
後述するのですが、C++
のビルド時に参照するCMakeLists.txt
を見ると、16KB ページサイズ
でビルドするように追記されてました
がんばろう
macOS / Linux
の場合はシェルスクリプトで確認できます。適当にテキストファイルを.sh
で作り、シェルスクリプトを貼り付けてください。vim
だとペーストモードが便利。
Linux
しか確認できていませんが、chmod
で実行権限をつけた後、./check_elf_alignment.sh {APKのパス}
コマンドで動くはず。
chmod +x check_elf_alignment.sh
./check_elf_alignment.sh app-release.apk
Windows
の場合はWSL2
か、あとはコマンドを愚直に叩く方法もある→ Support 16 KB page sizes | Compatibility | Android Developers
https://developer.android.com/guide/practices/page-sizes
GitBash
じゃ動かなかった、unzip dir lib/*
の箇所でエラーになるのと、GitBash
でもobjdump
がwindows
に無い。以下が実行結果です、UNALIGNED
が16KB ページサイズ
未対応で、ALIGNED
が対応済みです。もっぱら自分が用意した.so
が未対応ですね、、
/tmp/app-release_out_6q4W9/lib/arm64-v8a/libandroid_jni.so: UNALIGNED (2**12)
/tmp/app-release_out_6q4W9/lib/arm64-v8a/libandroid_jni_without_simd.so: UNALIGNED (2**12)
/tmp/app-release_out_6q4W9/lib/arm64-v8a/libandroidx.graphics.path.so: ALIGNED (2**14)
/tmp/app-release_out_6q4W9/lib/arm64-v8a/libjnidispatch.so: ALIGNED (2**16)
/tmp/app-release_out_6q4W9/lib/arm64-v8a/libandroid_rust_uniffi.so: UNALIGNED (2**12)
対応する必要があるCPU アーキテクチャ
はARM 64 ビット(arm64-v8a)
、x86_64
です。32bit
はいいらしい。
この3つ?
Android Studio
でCMakeLists.txt
を使ってC++
をビルドしている場合C++
プロジェクトをAndroid NDK
でビルドして.so
と.h
をAndroid Studio
へ持ち込んでいる場合Android NDK
でRust
とかからクロスコンパイルしている場合Android NDK r27
を使います。
後述しますがr28
を使えれば一番早いです。
Android NDK r28
以上を使って、あとはいつも通りビルドする。
バージョンが上げられる場合は多分これが一番早い。
Android Studio
以外でビルドする場合はNDK
のパスをr28
に置き換えれば良さそう。Android Studio
でC++
ビルドする場合は、app/build.gradle (.kts)
でndkVersion = ""
を28
のものにする必要があります。以下例。
android {
// 以下省略...
defaultConfig {
// 以下省略...
ndkVersion = "28.0.13004108"
}
// 以下省略...
}
一番多そう雰囲気(なぜか変換できる)。
C++
コードをAndroid Studio
で書いている場合。
この場合はapp
フォルダ内のbuild.gradle (.kts)
にarguments += listOf("-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON")
を書き足せばいいはず。
多分こう。
android {
namespace = "io.github.takusan23.cppnativesample"
compileSdk = 35
defaultConfig {
applicationId = "io.github.takusan23.cppnativesample"
minSdk = 21
targetSdk = 35
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
// これを足す
// This block is different from the one you use to link Gradle
// to your CMake or ndk-build script.
externalNativeBuild {
// For ndk-build, instead use the ndkBuild block.
cmake {
// Passes optional arguments to CMake.
arguments += listOf("-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON")
}
}
}
// 以下省略...
これでAPK
を作成しもう一度チェックしてみると、ちゃんとarm64-v8a
、x86_64
でALIGNED
になってました。
takusan23@DESKTOP-ULEKIDB:~$ ./check_elf_alignment.sh app-release-cpp.apk
Recursively analyzing app-release-cpp.apk
NOTICE: Zip alignment check requires build-tools version 35.0.0-rc3 or higher.
You can install the latest build-tools by running the below command
and updating your $PATH:
sdkmanager "build-tools;35.0.0-rc3"
=== ELF alignment ===
/tmp/app-release-cpp_out_BGTqn/lib/x86_64/libcppnativesample.so: ALIGNED (2**14)
/tmp/app-release-cpp_out_BGTqn/lib/arm64-v8a/libcppnativesample.so: ALIGNED (2**14)
/tmp/app-release-cpp_out_BGTqn/lib/x86/libcppnativesample.so: UNALIGNED (2**12)
/tmp/app-release-cpp_out_BGTqn/lib/armeabi-v7a/libcppnativesample.so: UNALIGNED (2**12)
Found 2 unaligned libs (only arm64-v8a/x86_64 libs need to be aligned).
次はこのパターンAndroid Studio
以外でCMakeLists.txt
を使って共有ライブラリをビルドしている場合。
CMakeLists.txt
を使ってリンカーに引数をつければいいらしい。
というわけでAndroid
向けにビルドできる適当なプロジェクトで試します。今回はUltraHDR
画像を作るlibultrahdr
をビルドしてみる。Android
向けが公式にサポートされてるので。
(Android
の機能だからそれはそう)
building.md
に従えば出来るはず。cmake
とninja
をapt
からinstall
して、Android NDK
をダウンロードして、指示通りコマンドを叩くだけ感。
しかし、これでビルドして.so
をAPK
に入れても16K ページサイズ
に対応してないのでUNALIGNED
になります。
/tmp/app-release-other-project_out_7tCyK/lib/arm64-v8a/libuhdr.so: UNALIGNED (2**12)
というわけでAndroid
ビルドで使ってる?CMakeLists.txt
を探します。libultrahdr
の場合はandroid.cmake
がそれだったので、一番下にこれを書き足しました。target_link_options
はなんかエラーになってしまった。
# Android 15 16K page-size support.
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,-z,max-page-size=16384")
あとはこれでビルドすればいいはず。出来た.so
をmain/jniLibs
のCPU アーキテクチャ
のところに格納して終わりのはず。
以下は面倒くさがってarm64-v8a
しかビルドしてませんが、これでALIGNED
になりました。libuhdr.so
です。
takusan23@DESKTOP-ULEKIDB:~$ ./check_elf_alignment.sh app-release-other-project-16k.apk
Recursively analyzing app-release-other-project-16k.apk
NOTICE: Zip alignment check requires build-tools version 35.0.0-rc3 or higher.
You can install the latest build-tools by running the below command
and updating your $PATH:
sdkmanager "build-tools;35.0.0-rc3"
=== ELF alignment ===
/tmp/app-release-other-project-16k_out_EFo2u/lib/x86_64/libcppnativesample.so: ALIGNED (2**14)
/tmp/app-release-other-project-16k_out_EFo2u/lib/arm64-v8a/libcppnativesample.so: ALIGNED (2**14)
/tmp/app-release-other-project-16k_out_EFo2u/lib/arm64-v8a/libuhdr.so: ALIGNED (2**14)
/tmp/app-release-other-project-16k_out_EFo2u/lib/x86/libcppnativesample.so: UNALIGNED (2**12)
/tmp/app-release-other-project-16k_out_EFo2u/lib/armeabi-v7a/libcppnativesample.so: UNALIGNED (2**12)
Found 2 unaligned libs (only arm64-v8a/x86_64 libs need to be aligned).
=====================
Rust
のcargo
はその他のビルドシステム
に当たるので、-Wl,-z,max-page-size=16384
をどうにかして渡す必要があります。
cargo build
の際に、RUSTFLAGS
で-C link-arg=
に突っ込めば良さそう感。なのでこれでいいはず。
RUSTFLAGS='-C link-arg=-Wl,-z,max-page-size=16384' cargo build --release --target aarch64-linux-android
RUSTFLAGS='-C link-arg=-Wl,-z,max-page-size=16384' cargo build --release --target x86_64-linux-android
できた.so
を同様にjniLibs
に配置すればいいはず。APK
作ってチェックしてみたけど大丈夫そう。libandroid_rust_jni.so
がx86_64
とarm64-v8a
でALIGNED
になってます!!
takusan23@DESKTOP-ULEKIDB:~$ ./check_elf_alignment.sh app-release-rust-jni.apk
Recursively analyzing app-release-rust-jni.apk
NOTICE: Zip alignment check requires build-tools version 35.0.0-rc3 or higher.
You can install the latest build-tools by running the below command
and updating your $PATH:
sdkmanager "build-tools;35.0.0-rc3"
=== ELF alignment ===
/tmp/app-release-rust-jni_out_bm5NF/lib/x86_64/libandroid_rust_jni.so: ALIGNED (2**14)
/tmp/app-release-rust-jni_out_bm5NF/lib/x86_64/libandroidx.graphics.path.so: ALIGNED (2**14)
/tmp/app-release-rust-jni_out_bm5NF/lib/arm64-v8a/libandroid_rust_jni.so: ALIGNED (2**14)
/tmp/app-release-rust-jni_out_bm5NF/lib/arm64-v8a/libandroidx.graphics.path.so: ALIGNED (2**14)
/tmp/app-release-rust-jni_out_bm5NF/lib/x86/libandroid_rust_jni.so: UNALIGNED (2**12)
/tmp/app-release-rust-jni_out_bm5NF/lib/x86/libandroidx.graphics.path.so: ALIGNED (2**14)
/tmp/app-release-rust-jni_out_bm5NF/lib/armeabi-v7a/libandroid_rust_jni.so: UNALIGNED (2**12)
/tmp/app-release-rust-jni_out_bm5NF/lib/armeabi-v7a/libandroidx.graphics.path.so: ALIGNED (2**14)
Found 2 unaligned libs (only arm64-v8a/x86_64 libs need to be aligned).
エミュレーターで試せるらしい。
起動できた。
逆に16KB ページサイズ
に対応してないと以下のような例外で落ちる。
FATAL EXCEPTION: main
Process: io.github.takusan23.androidrustjni, PID: 16320
java.lang.UnsatisfiedLinkError: dlopen failed: empty/missing DT_HASH/DT_GNU_HASH in "/data/app/~~S5iS_B60dhn6WlqTEzvegg==/io.github.takusan23.androidrustjni-HG0y1lLFYvVg6MvQNC3k2g==/lib/x86_64/libandroid_rust_jni.so" (new hash type from the future?)
at java.lang.Runtime.loadLibrary0(Runtime.java:1081)
at java.lang.Runtime.loadLibrary0(Runtime.java:1003)
at java.lang.System.loadLibrary(System.java:1765)
at io.github.takusan23.androidrustjni.MainActivity.<clinit>(MainActivity.kt:42)
at java.lang.Class.newInstance(Native Method)
at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:95)
at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:44)
at android.app.Instrumentation.newActivity(Instrumentation.java:1448)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3941)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4235)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:112)
at android.app.servertransaction.TransactionExecutor.executeNonLifecycleItem(TransactionExecutor.java:174)
at android.app.servertransaction.TransactionExecutor.executeTransactionItems(TransactionExecutor.java:109)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:81)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2636)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loopOnce(Looper.java:232)
at android.os.Looper.loop(Looper.java:317)
at android.app.ActivityThread.main(ActivityThread.java:8705)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:886)
で書かれてる通り、16KB ページサイズ
でビルドした共有ライブラリは4KB デバイス
でも動くそうな。
一応古い端末を起動して試してみたけど、Galaxy S7 Edge
(Snapdragon 820 (64bit) / Android 7
)で起動できた。16KB ページサイズ
の.so
に置き換えるで問題なさそう。
おわりです。お疲れ様でした。888