[Flutter]Android実機でOpenCV(C++,FFI)を搭載してデバッグビルドする方法

この記事では、備忘録も兼ねて、FlutterにおいてAndroid実機でOpenCV(C++,FFI)を搭載してデバッグビルドする方法と、それに付随して発生する問題やその解決法について記述する。

この方法でOpenCVを搭載するメリットについては、iOSに向けてビルドする場合とAndroidに向けてビルドする場合とで(KotlinやSwiftなどに)コードを分ける必要が無く、メソッドチャンネルなどの理解が難しい手法を取る必要もないとだけ簡潔に述べておく。

## 最初にビルドを行うまでの作業

[Integrating C library in a Flutter app using Dart FFI | by Igor Kharakhordin | Flutter Community | Medium](https://medium.com/flutter-community/integrating-c-library-in-a-flutter-app-using-dart-ffi-38a15e16bc14)  
まずはここを見るべし。
大前提として、OpenCVのライブラリをプロジェクトにブチ込んだり、ライブラリの依存関係をどう記述するかについての情報が載っている。なお、iOSの場合についても書いてある。

ただし、当ページの

> In order to build and link the library and our code from _native\_opencv.cpp_ must be added to cmake (or ndk-build). Create _CMakeLists.txt_ configuration file in _android_ folder of the plugin:

の次に書いてある

```
cmake_minimum_required(VERSION 3.4.1)

include_directories(../include)
add_library(lib_opencv SHARED IMPORTED)
set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_java4.so)

add_library(native_opencv SHARED ../ios/Classes/native_opencv.cpp)
target_link_libraries(native_opencv lib_opencv)
```

において、`../include`と`../ios/Classes/native_opencv.cpp`に関しては、**もう一つ**`../`**を付けないとパスが合わない**と思われるので注意。
ここがミスっていると、前者は「**opencvが見つからないぜ！**」と言われ、後者は「**C++のソースコードが無いぜ！**」と言われる。

ここまでで無事にビルドできれば万々歳だが、自分が作業を行った際は追加で

```
FAILURE: Build failed with an exception. 

* What went wrong:
Execution failed for task ':app:stripDebugDebugSymbols'.
> No toolchains found in the NDK toolchains folder for ABI with prefix: arm-linux-androideabi
```

という内容のエラーが発生した。以下に参考ページと、原因と思われるものを記述する。

[NDK issue after flutter upgrade, No version of NDK matched the requested version 20.0.5594570. Versions available locally: 21.0.6113669 · Issue #53657 · flutter/flutter · GitHub](https://github.com/flutter/flutter/issues/53657)

## Gradle Pluginのバージョンが古い
今回の結論ではそうなった。プロジェクトを作ったのが半年以上前で、諸々の設定が古かったということも原因として考えられるため、最新の環境でプロジェクトを作り、上記の作業を行った場合は以下の作業が必要ない可能性もある。
あるいは、Gradle Pluginとその他の環境との食い合わせが悪かった可能性もある。

なんにせよ、今回採った対策としては、
`(ProjectRoot)/android/build.gradle`において、

```
buildscript {
	dependencies {
		classpath 'com.android.tools.build:gradle:4.0.0'
	}
}
```

とし、Gradle Pluginのバージョンを`4.0.0`以上にする(変更箇所以外は省略してある)。  
最終的には`4.0.1`や`4.0.2`でも通ったが、あまり重要ではないので割愛。

#### Gradleのバージョンも古い

しかし、これだけだと

```
> Failed to apply plugin [id 'com.android.internal.version-check'] > Minimum supported Gradle version is 6.1.1. Current version is *.*.*. If using the gradle wrapper, try editing the distributionUrl in (ProjectRoot)/android/gradle/wrapper/gradle-wrapper.properties to gradle-6.1.1-all.zip
```

とツッコまれる。

要約すると、「**Gradleのバージョンが古いぜ！**」ということ。

さっきGradle Pluginのバージョンを上げたのに、こっちではGradleのバージョンが古いとはどういうこっちゃ？　となるが、[Gradle と Gradle Plugin のバージョンについて - Qiita](https://qiita.com/izuki_y/items/3c20cd1c655fc4fc6127)によれば、Gradle Pluginとは

> これはgradleって書いていますがAndroid plugin for Gradle(人によっては Gradle Plugin)と呼ばれているものでGradleそのものではありません。  
> 簡単に言えば_GraldeをAndroid Studioで使用するためのプラグイン_です。

とのこと。つまりは別物。

とりあえず、上記のエラーログにはどこをどう変更すれば良いのか親切に書いてあるので、指摘通りに`(ProjectRoot)/android/gradle/wrapper/gradle-wrapper.properties`を開き、

```
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
```

に変更。  

#### NDKのバージョンも古い
しかしこれでもまだ、

```
> No version of NDK matched the requested version 21.0.6113669. Versions available locally: 23.0.7123448
```

とツッコまれる。少々疲れてくるが、次で最後。

これは、[上記参考ページ](https://github.com/flutter/flutter/issues/53657 "NDK issue after flutter upgrade, No version of NDK matched the requested version 20.0.5594570. Versions available locally: 21.0.6113669 · Issue #53657 · flutter/flutter · GitHub")のdeakjahn氏がその答えを述べており、`(ProjectRoot)/android/app/build.gradle`(←先程の`build.gradle`とは別物なので注意)を

```
android {
	ndkVersion '23.0.7123448-rc1'
}
```

のように変更(該当箇所が無い場合は追記)することでビルドが通るようになる。

ただし、[上記参考ページ](https://github.com/flutter/flutter/issues/53657 "NDK issue after flutter upgrade, No version of NDK matched the requested version 20.0.5594570. Versions available locally: 21.0.6113669 · Issue #53657 · flutter/flutter · GitHub")でdeakjahn氏が述べているように、**GradleやGladle Plugin、NDKのバージョンを変更するたびにこの部分で引っかかる可能性がある**。とはいってもビルドが通らないのはもっと困るので、今後注意を払うしかない。

ここで指定されるバージョンのNDKがインストールされているかどうかは、AndroidStudioを起動し、(Macでは)出てきたウインドウの右下から`Configure > SDK Manager > Android SDK`の`SDK Tools`タブにおいて、右下の`Show Package Details`にチェックを入れ、`NDK`のツリーを見ればわかる。
該当するバージョンにチェックが入っていない場合はインストールされていないので、チェックを入れて`Apply`を押せばインストールウィザードが開始する。

なお、`Version`欄に記載されているバージョン名に、`rc1`など純粋な番号以外の記号が付属している場合、上記のようにハイフンでつないで記述する。一応忘れてもエラーログで記法を教えてくれる。

**ここまでの作業を終えてなおビルドが通らない場合は、なにか別の原因が存在する可能性がある**ため、エラーログをよく確認することを推奨する。

## 蛇足
[Gradleのバージョンも古い](#Gradleのバージョンも古い)　では、

```
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
```

としたが、[書かれているURL](https://services.gradle.org/distributions/ "Gradle Distributions")にアクセスすれば、より新しいバージョンのGradleが配布されていることがわかる。今後の面倒を減らすためにも、6.1.1より新しいバージョンを入れておいてもいいかもしれない。

2021-03-04現在、6.8.3での動作を確認済み。