たくさんの自由帳
Androidのお話
たくさんの自由帳
文字数(だいたい) : 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 ページサイズに対応する必要があります。

Obsługa stron o rozmiarze 16 KB | Compatibility | Android Developers
Przygotuj aplikacje na Androida na strony o rozmiarze 16 KB, aby spełniały wymagania Google Play dotyczące zgodności z Androidem 15 i nowszymi wersjami.
https://developer.android.com/guide/practices/page-sizes?hl=pl
リリース 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.apkWindowsの場合はWSL2か、あとはコマンドを愚直に叩く方法もある→ 
Support 16 KB page sizes | Compatibility | Android Developers
Prepare Android apps for 16 KB page sizes to meet Google Play compatibility requirements for Android 15 and higher.
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