effidevFlutter・Cloudflareエッジ・クラウドコスト最適化

2026年5月の期限を過ぎてもGoogle PlayがFlutterアプリを拒否してくる場合:16KBページサイズ実践対応ガイド

2026年5月の期限を過ぎてもGoogle PlayがFlutterアプリを拒否してくる場合:16KBページサイズ実践対応ガイド

「たしか去年対応を終えたはずなのに、なぜまた拒否されるんだ?」——ここ数週間、Flutter開発者コミュニティやイシュートラッカーで繰り返し見かける質問です。Google Playの16KBメモリページサイズ要件は、当初2025年11月1日が施行日で、個別に申請すれば最大2026年5月31日まで猶予を受けることができました。今日は2026年7月24日なので、この猶予期間すらすでに終わってから2か月近く経っています。それでも依然として多くのアプリが、アップデートをアップロードする際に拒否されています。理由はほぼ一つです。アプリ自体のコードは対応済みでも、依存しているサードパーティのネイティブプラグインがまだ16KBに整列(アライン)されていないためです。

この記事は単なる告知的なまとめではなく、今まさにこの問題で行き詰まっている人のための実践対応ガイドです。

要点まとめ

  • Google PlayはAndroid 15(API 35)以上をターゲットとし、ネイティブコード(.so)を含むアプリの新規アプリ/アップデートに対して16KBページアラインメントを要求します。純粋なDartコードのみのアプリは影響を受けません。
  • 当初の期限は2025年11月1日で、個別に猶予を申請すれば最大2026年5月31日まで延長できました。この時点以降は猶予なしで即座に適用されます。
  • Flutterエンジン(libflutter.solibapp.so)自体はFlutter 3.32.8以上で16KBに対応しています。問題の大半はサードパーティプラグインがバンドルしている.soファイルにあります。
  • zipalignllvm-objdump、Play Consoleのアプリバンドルエクスプローラーで事前に自分で検証できます。
  • CIにアラインメント検証ステップを入れておかないと、この問題は必ず再発します。

16KBページサイズ要件はなぜ生まれ、誰に適用されるのか

従来、Androidはメモリを4KB単位(ページ)で管理してきました。Android 15からAOSPは16KBページサイズをサポートし始め、以降に発売されるハイスペック機種(RAM容量の大きい端末)は標準で16KBページを使う方向に移行しつつあります。Googleが公式ドキュメント(developer.android.com/guide/practices/page-sizes)で示している自社テスト基準のパフォーマンス改善幅は以下の通りです(ただし、Google自身も「初期テスト結果であり、実機では結果が異なる場合がある」と明記しています)。

こうした数値を根拠に、Googleは2025年11月1日以降、Android 15(API 35)以上をターゲットとする新規アプリおよび既存アプリのアップデートは、64ビット端末で16KBページサイズをサポートしなければならないと明言しました。その後、複数の開発者コミュニティのスレッド(Google Play開発者コミュニティの「Clarification on 16 KB page size extension」など)で確認できるように、Play Consoleで個別に猶予を申請すれば2026年5月31日まで一時的に延期することが可能でした。この記事を書いている現在(2026年7月)は、その猶予期間すら終わった時点であり、対応していないアプリはアップデートのアップロード段階でそのままブロックされます。

重要なのは、ネイティブコードを含まない純粋なDart/Flutterアプリには一切対応が不要だという点です。Google公式ブログも「ネイティブコードを含まないアプリは変更不要で、すでに互換性がある」と明記しています。問題になるのは、カメラ、アニメーション、DB、地図、決済SDKなどのために.soファイルを含むネイティブプラグインを使っている、実務上大多数のFlutterアプリの方です。

自分のアプリが影響を受けるか確認する方法:3段階診断

ステップ1 — まずネイティブプラグインの一覧を洗い出す

pubspec.yamlの依存関係のうち、ネイティブコードを含んでいる可能性があるパッケージをまず洗い出します。カメラ、DB(sqlite、realm、isarのネイティブバックエンド)、地図(google_maps_flutter)、アニメーション(rive、lottieのネイティブレンダラー)、画像処理(flutter_image_compress)、決済・セキュリティSDK、クラッシュレポーター(sentry_flutter、firebase_crashlytics)、WebRTC、ML推論(tflite、onnxruntime)系のパッケージが代表的な「容疑者」です。

ステップ2 — ビルド成果物の中の.soファイルを直接確認する

疑わしいというだけでは不十分なので、実際のビルド結果物を開いて確認します。

# release ビルドをまず作成する
flutter build appbundle --release

# バンドル内に含まれる .so 一覧を確認
unzip -l build/app/outputs/bundle/release/app-release.aab | grep "\.so"

.soが16KBに整列(アライン)されているかは、llvm-objdumpで直接確認できます(macOS向けのパス例):

$ANDROID_HOME/ndk/<NDK_VERSION>/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-objdump \
  -p path/to/librive_text.so | grep LOAD

出力でalign 2**14(16384バイト)であれば正常で、2**13(8192)以下であれば4KBアラインメントのため問題になります。AAB全体を一度にまとめて確認したい場合はbundletoolも便利です。

bundletool dump config --bundle=app-release.aab | grep alignment
# PAGE_ALIGNMENT_16K なら正常、PAGE_ALIGNMENT_4K なら未対応

APK単位ではzipalignの検証モードを使います。

$ANDROID_HOME/build-tools/35.0.0/zipalign -v -c -P 16 4 app-release.apk

Android Studioを使っているなら、File > Profile or Debug APK(または Build > Analyze APK)で開いてlib/arm64-v8aフォルダのAlignmentカラムを見るのが最も手早い方法です。警告アイコンが付いているファイルこそが犯人です。

ステップ3 — Play Consoleへのアップロード前の事前検証

Play Consoleの**アプリバンドルエクスプローラー(App Bundle Explorer)**は、AABをアップロードすると16KBに整列していないライブラリを自動的に表示してくれます。実際の拒否事例で繰り返し見られるメッセージの形は次の通りです。

メッセージにはファイル名がそのまま表示されるので、その.soがどのプラグイン由来なのかを逆にたどるだけで済みます。

Flutter / NDK / AGP バージョン互換マトリクス

構成要素 最小要件 推奨 備考
Flutter SDK 3.32.8以上 最新のstable(3.35以上) 3.32.8からエンジン自体が16KBに対応。flutter doctor -vで現在のバージョンを確認
Android NDK r27以上 r28系 r28からデフォルトで16KBアラインメントでコンパイルされる。r27はリンカーフラグを手動で追加する必要がある
AGP(Android Gradle Plugin) 8.5.1以上 最新の8.7.x以上 8.3〜8.5は16KBアラインメントは可能だが、bundletoolがAPKを自動でzipalignしない
Gradle 8.9以上 AGPバージョンに対応した最新版 AGP 8.5.1との互換組み合わせ基準
Android SDK Build-Tools 35.0.0以上 最新 zipalign -P 16オプションはこのバージョンから対応
compileSdk / targetSdk 35 35以上 Android 15のAPIレベル

一つ落とし穴があります。2026年7月時点で、Flutterのflutter_tools GradleプラグインはデフォルトのNDKバージョンとして27.0.12077973を指定しています(flutter/flutterのイシュー#175022で確認された内容)。r27は要件を満たす最低ラインではあるものの、r27はデフォルトでは16KBアラインメントでコンパイルしないため、安全を期すなら自分でリンカーフラグを追加するか、NDKをr28系に上げる必要があります。android/app/build.gradle(または.kts)で次のように指定します。

android {
    ndkVersion "28.2.13676358" // 다운로드 페이지에서 최신 r28.x 확인 후 대체

    defaultConfig {
        externalNativeBuild {
            cmake {
                // r27을 그대로 써야 한다면 이 플래그를 추가
                arguments "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
            }
        }
    }
}

AGPが8.0以下のレガシープロジェクトの場合、gradle.propertiesに次の一行も必要です。

android.bundle.enableUncompressedNativeLibs=false

もう一つ、実務でよく報告されているバグがあります。Flutter 3.32.8/3.35.1と特定のAGPの組み合わせで、minSdkVersionを23以上に上げると、新規作成したプロジェクトでもアプリバンドルエクスプローラーが16KB警告を出す現象です(flutter/flutterのイシュー#173949)。minSdkVersionを21以下に下げると警告は消えますが、これは根本解決ではなく回避に過ぎないため、Flutter/AGPを最新の安定版に合わせることを優先することをお勧めします。

未対応のサードパーティプラグインに出会ったとき:実際の事例と対応戦略

最も頭が痛い状況は、自分のコードはすべて修正し終えたのに、依存しているプラグインのベンダーがまだ.soを再ビルドしていない場合です。実際に確認されている事例を2つ見てみましょう。

事例1 — Riveアニメーション。 従来のriveパッケージ(0.13.x系)はlibrive_text.soが4KBアラインメントでビルドされているため、Play Consoleでそのまま拒否されます。rive-app/rive-flutterリポジトリには、この要望がイシュー#458、#488、#524、#539、#547、#553として1年以上にわたって繰り返し上がっています。実際の解決策は、旧バージョンのriveパッケージにこだわり続けることではなく、後継パッケージであるrive_nativeへ移行することです。rive_nativeはバージョン0.0.3から、イシュー#479を根拠に「Android: support 16 KB page sizes」をchangelogに明記しています。

事例2 — SQLite。 sqlite3_flutter_libsはバージョン0.5.25でchangelogに「Support 16KiB page sizes on Android 15」と明記して解決済みです。ただし現在(0.6.0+eol)、このパッケージ自体がdeprecatedの状態にあり、changelogは「sqlite3 3.x以降ではsqlite3_flutter_libsはもはや不要」と案内しています。つまり、古いプロジェクトであるほど、パッケージを最新に上げること自体がすでに解決策になっているケースが多いのです。

この2つの事例から得られる実践的な対応手順は次の通りです。

  1. まず最新バージョンへのアップグレードを試す。 人気パッケージの大半はすでに対応版をリリースしているか、リリース準備中です。changelogで「16KB」「page size」「alignment」といったキーワードをまず検索してください。

  2. dependency_overridesで特定のtransitive依存関係のバージョンを強制する。 最上位のパッケージ自体は更新されていなくても、その内部のネイティブバインディングパッケージだけ新バージョンが出ている場合に有効です。

    dependency_overrides:
      rive_native: ^0.0.5
  3. Gradleのexclude + 最新AARを直接指定。 ベンダーが別のMavenアーティファクトとして新しい.soを配布している場合、プラグインが引き込んでくる旧バージョンのaarを除外し、新バージョンを直接依存関係として追加できます。

    implementation("com.example:some-native-sdk:2.3.0") {
        exclude group: "com.example", module: "some-native-sdk-legacy"
    }
  4. jniLibsソースセットで.soを直接差し替える。 コミュニティが再コンパイルした16KBアラインメント済みバイナリを入手した、あるいは自分でNDKを使って再ビルドした場合、ベンダーのaarより優先されるようソースセットを追加します。

    android {
        sourceSets {
            main {
                jniLibs.srcDirs += ["src/main/jniLibs16k"]
            }
        }
        packagingOptions {
            pickFirst "lib/arm64-v8a/librive_text.so"
        }
    }
  5. それでもダメならフォークする。 プラグインがオープンソースであれば、リポジトリをフォークしてNDKバージョンだけr28に上げて再ビルドし、pubspec.yamlでgitパスとして参照するのも現実的な一時しのぎになります。

    dependencies:
      rive:
        git:
          url: https://github.com/<your-org>/rive-flutter.git
          ref: fix/16kb-alignment
  6. 最後の手段として代替ライブラリの検討。 ベンダー対応の目処が立たない場合、機能の近い別のパッケージに乗り換えるのも選択肢です(例: 古いアニメーションライブラリの代わりにLottie系を検討)。

端末側の「16KBバックコンパットモード」(adb shell setprop bionic.linker.16kb.app_compat.enabled true)は、あくまで開発中のテストを便利にするためのものであり、Play Console審査を通してくれる仕組みではないことをはっきりさせておく必要があります。このモードはユーザー端末側の設定であって、皆さんが配布するビルド自体の属性ではありません。

CIパイプラインに16KBアラインメントの自動検証を追加する

一度直したからといって終わりではありません。プラグインのバージョンを再度下げたり、新しいチームメンバーが未整列のSDKを追加したりすれば再発します。CIでリリースビルドのたびに自動でアラインメントを検証し、Play Consoleにアップロードする前に失敗させることが再発防止の要です。以下はGitHub Actionsの例です。

name: android-release-check

on:
  push:
    branches: [main]

jobs:
  check-16kb-alignment:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: subosito/flutter-action@v2
        with:
          channel: "stable"

      - name: Build App Bundle
        run: flutter build appbundle --release

      - name: Download bundletool
        run: |
          BUNDLETOOL_URL=$(curl -s https://api.github.com/repos/google/bundletool/releases/latest \
            | grep "browser_download_url.*jar" | cut -d '"' -f 4)
          curl -sL -o bundletool.jar "$BUNDLETOOL_URL"

      - name: Extract universal APK
        run: |
          java -jar bundletool.jar build-apks \
            --bundle=build/app/outputs/bundle/release/app-release.aab \
            --output=out.apks --mode=universal
          unzip -o out.apks -d out_apks

      - name: Verify 16KB page alignment
        run: |
          ZIPALIGN=$ANDROID_HOME/build-tools/35.0.0/zipalign
          $ZIPALIGN -v -c -P 16 4 out_apks/universal.apk || \
            (echo "::error::16KB 페이지 정렬 실패 — .so 파일을 확인하세요" && exit 1)

このステップが失敗すると、どの.soが問題なのかがzipalignの出力にそのまま表示されるため、そのファイル名からどのプラグインが原因かをすぐに特定できます。チーム規模がある程度大きいなら、RenovateやDependabotを使ってrive_nativesqlite3google_maps_fluttersentry_flutterのように過去に問題のあったネイティブパッケージの更新PRを自動で受け取るようにしておくのも、再発防止に役立ちます。

最後のチェックリスト

今この問題で行き詰まっているなら、原因の大半は皆さんのコードではなく、古いサードパーティのネイティブ依存関係にあります。上記の手順を一つずつ確認していけば、たいていは1日以内に原因を特定できるはずです。