👻

Flutter for Android の ABI (armeabi-v7a と arm64-v8a)

2020/12/17に公開

Flutterはエンジンの低レイヤの部分はC++で書かれています。Androidでは、C/C++部分はlibflutter.soとしてアプリ内に含まれます。Flutter SDKにはビルド済みのlibflutter.soファイルが含まれており、これがそのまま使われるようにアプリはビルドされるため、アプリのビルド時にはNDKは必要ありません。

さて、FlutterでAndroid向けにAPKファイルをビルドする際、ある特定のABIのlibflutter.soファイルのみがAPKファイルに含まれます。デフォルトでは armeabi-v7a です。armeabi-v7a は32bitでも64bitでも実行できるので、多くのAndroidデバイスで実行できます。

一方で、複数のABIを含むユニバーサルなAPKを作ることが、現時点ではできません。

しかし Google Play Store は64bit対応を強く推進しており、64bitの arm64-v8a を使いたい(publishしたい)というニーズもあります。

※念のために書いておきますと armeabi-v7a でも64bit CPUのAndroid端末で実行できます

target-platform

この場合は —target-platform というオプションを使います。

armeabi-v7a をビルド(デフォルト):

flutter build apk —target-platform=android-arm

※指定しない場合のデフォルトですが、明示しています

arm64-v8a をビルド:

flutter build apk —target-platform=android-arm64

APKの中身を見ると、たしかに arm64-v8a に libflutter.so が入っています。

<img width="287" alt="スクリーンショット 2019-02-19 19.36.14.png" src="/posts/qiita-3e2914d007e990af947c/1.png">

※NDKを使わないアプリでは libflutter.so のみになります。スクリーンショットで他の .so があるのはNDKを使っているため。

このようにして、それぞれのAPKファイルができるので、これをPlay Storeに公開すればよいです。

Makefileで以下のようにしておくと、楽だと思います。

android: android-arm android-arm64

android-arm:
	flutter build apk --target-platform=android-arm
	cp build/app/outputs/apk/release/app-release.apk ~/Downloads/myapp-release-arm.apk

android-arm64:
	flutter build apk --target-platform=android-arm64
	cp build/app/outputs/apk/release/app-release.apk ~/Downloads/myapp-release-arm64.apk

ただし、それらのAPKファイルのversionCodeは違っている必要があることは留意してください。gradle内でこのオプションの値を project.property('target-platform') で取得できるので、それで分岐させるとよいと思います。

abiFilters

※これより下が必要になるのはNDKを使っている場合に必要なabiFiltersのための処理についてです。NDKなしでFlutterを使っている場合には不要かもしれません。

CARTUNEではOpenCVを使うためにNDKを使っており、ndkのabiFiltersをFlutterのオプションと揃える必要があるため、gradleファイルで以下のように分岐させています。

versionCode 10203000
versionName "1.2.3"

ndk {
    if (!project.hasProperty('target-platform'))
        abiFilters 'x86'
    else switch (project.property('target-platform')) {
        case 'android-arm64':
            abiFilters 'arm64-v8a'
            versionCode += 2
            break
        case 'android-arm':
            abiFilters 'armeabi-v7a'
            versionCode += 1
            break
        default:
            abiFilters 'x86'
            break
    }
}

※x86があるのは、開発中の flutter run コマンドでエミュレータで起動するため

versionCodeの下一桁をABIのために空けておき、target-platformの分岐で += して切り替えています。

IntelliJ

また、コマンドではなく IntelliJ でアプリを実行する際に、このtarget-platformの切り替えを行うためには、メインメニューの「Run」>「Edit Configurations...」で表示される以下のダイアログでAdditional argumentsに設定しておきます。

<img width="973" alt="2019-02-1813-169a6b89-a622-4de9-bace-4a8fb5bdb5f7.17.27.png" src="/posts/qiita-3e2914d007e990af947c/2.png">

エミュレータ用と実機用で別のConfigurationを用意しておいて、実行時にプルダウンから切り替えて使います。

splits

ちなみにGradleのsplitsで複数のAPKを作ろうとすると、flutter build コマンドは失敗します。

これは、flutterコマンドが出来上がったAPKファイルの有無をチェックしており、splitsでアーキテクチャ名がファイル名に入ることと相性が悪いためです。

buildディレクトリ以下にAPKファイルが生成されるところまでは行くので、エラーを無視してAPKファイルを使うというのはありかもしれません。

この記事はQiitaの記事をエクスポートしたものです

Discussion