Flutter

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

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 が入っています。

スクリーンショット 2019-02-19 19.36.14.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に設定しておきます。

2019-02-1813-169a6b89-a622-4de9-bace-4a8fb5bdb5f7.17.27.png

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


splits

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

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

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