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を作ることが、現時点ではできません。

- [https://github.com/flutter/flutter/issues/10728](https://github.com/flutter/flutter/issues/10728)
- [https://github.com/flutter/flutter/issues/23055](https://github.com/flutter/flutter/issues/23055)
- [https://github.com/flutter/flutter/issues/24534](https://github.com/flutter/flutter/issues/24534)

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

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

## target-platform

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

armeabi-v7a をビルド（デフォルト）:

```shell
flutter build apk —target-platform=android-arm
```

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

arm64-v8a をビルド:

```shell
flutter build apk —target-platform=android-arm64
```

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

<img width="287" alt="スクリーンショット 2019-02-19 19.36.14.png" src="https://qiita-image-store.s3.amazonaws.com/0/21341/121fe1d2-19c9-b194-3aed-141d38019c63.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ファイルで以下のように分岐させています。

```groovy
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="https://qiita-image-store.s3.amazonaws.com/0/21341/7473f886-438e-1075-976e-48315ef2dfd7.png">

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

## splits

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

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

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