Android Studio 2.1.2 で C++ ライブラリ

Android Studio 2.1.2 で C++ ライブラリを作ってみた

# 2016-07: Android Studio の C++ 対応状況

1. Android Studio で新しいプロジェクトを作ると [Gradle Plugin](http://tools.android.com/tech-docs/new-build-system) の[バージョン](https://developer.android.com/studio/releases/gradle-plugin.html)は 2.1.2 、 Grade のバージョンは 2.10 になってる
    1. Gradle の[最新は 2.14.1](https://gradle.org/gradle-download/)
    2. Gradle 2.11 以降のリリースノートでは特に Android や C++ に関する言及は無い: [2.14.1](https://docs.gradle.org/2.14.1/release-notes), [2.14](https://docs.gradle.org/2.14/release-notes), [2.13](https://docs.gradle.org/2.13/release-notes), [2.12](https://docs.gradle.org/2.12/release-notes), [2.11](https://docs.gradle.org/2.11/release-notes)
2. [Experimental Gradle Plugin](http://tools.android.com/tech-docs/new-build-system/gradle-experimental) に差し替えれば C++ をビルドできる
    1. [Google のサンプル](https://github.com/googlesamples/android-ndk/tree/master/hello-jni) では [gradle-experimental 0.7.0 を使ってるが](https://github.com/googlesamples/android-ndk/blob/master/hello-jni/build.gradle)、[0.7.2 がリリースされてる](https://bintray.com/android/android-tools/com.android.tools.build.gradle-experimental/view)
    2. 2016-08-08: ソースは[これ](https://android.googlesource.com/platform/tools/base/+/studio-master-dev/build-system/gradle-experimental)かな？
        1. チケットは[これ](https://android-review.googlesource.com/#/q/gradle-experimental)かな？ (でも 2016 年のチケットが見当たらない...)
    3. 2016-08-12: ソースは[ここ](https://bintray.com/android/android-tools/download_file?file_path=com%2Fandroid%2Ftools%2Fbuild%2Fgradle-experimental%2F0.7.2%2Fgradle-experimental-0.7.2-sources.jar)からもダウンロードできるっぽい
3. 従来の Glade Plugin と Experimental Plugin を[混在させることができる](http://frogermcs.github.io/json-parsing-with-flatbuffers-in-android/)
4. まだリリースされてない [Android Studio 2.2 では Gradle ではなく ndk-build または CMake を使った C++ 開発を改善する](http://android-developers.blogspot.jp/2016/05/android-studio-22-preview-new-ui.html)
    1. ndk-build: build.grade の externalNativeBuild で Android.mk を指定できる
    2. cmake: build.grade の externalNativeBuild で CMakeLists.txt を指定できる
    3. [コンパイルオプションなども指定できる](http://tools.android.com/tech-docs/external-c-builds)

# 方針

1. メインとなる Java コードに C++ を追加することを想定し
    1. Java アプリは従来の Glade Plugin でビルド
    2. C++ 用のライブラリモジュールを別に作って Experiment Gradle Plugin でビルド

2. Experimental Gradle Plugin の 0.7.2 を使ってみる

3. [Gradle は 2.10 のまま](http://tools.android.com/tech-docs/new-build-system/gradle-experimental?pli=1#TOC-Gradle-Requirements)

# 環境

1. Mac OS X 10.11.5 (英語)
2. Oracle JDK 1.8.0_91
3. Android Studio 2.1.2
4. Android NDK r12b

# 手順

1. Android Studio で Empty Activity の新しいプロジェクトを作成

2. ./build.gradle に gradle-experimental を追加

		// Top-level build file where you can add configuration options common to all sub-projects/modules.

		buildscript {
			repositories {
				jcenter()
			}
			dependencies {
				classpath 'com.android.tools.build:gradle:2.1.2'
				classpath 'com.android.tools.build:gradle-experimental:0.7.2'

				// NOTE: Do not place your application dependencies here; they belong
				// in the individual module build.gradle files
			}
		}

		allprojects {
			repositories {
				jcenter()
			}
		}

		task clean(type: Delete) {
			delete rootProject.buildDir
		}

3. 新しい Android Library モジュールを作成

4. ./app/build.gradle にライブラリモジュールへの依存を追加 (```compile project```)

			apply plugin: 'com.android.application'

			android {
				compileSdkVersion 24
				buildToolsVersion "24.0.0"

				defaultConfig {
					applicationId "jp.kenichi.androidapp04"
					minSdkVersion 15
					targetSdkVersion 24
					versionCode 1
					versionName "1.0"
				}
				buildTypes {
					release {
						minifyEnabled false
						proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
					}
				}
			}

			dependencies {
				compile project(':mylibrary')
				compile fileTree(dir: 'libs', include: ['*.jar'])
				testCompile 'junit:junit:4.12'
				compile 'com.android.support:appcompat-v7:24.0.0'
			}


5. ./mylibrary/build.gradle を gradle-experimental 用に書き換え


			apply plugin: 'com.android.model.library'

			model {
				android {
					compileSdkVersion 24
					buildToolsVersion "24.0.0"

					defaultConfig {
						minSdkVersion.apiLevel 15
						targetSdkVersion.apiLevel 24
						versionCode 1
						versionName "1.0"
					}
					//buildTypes {
					//    release {
					//        minifyEnabled false
					//        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
					//    }
					//}
					ndk {
						moduleName "mylibrary"
						platformVersion 15
						cppFlags.addAll(['-std=c++11', '-fexceptions', '-Wall'])
						ldLibs.addAll(['log'])
						stl = "gnustl_shared"
					}
				}
			}

			dependencies {
				compile fileTree(dir: 'libs', include: ['*.jar'])
				testCompile 'junit:junit:4.12'
				compile 'com.android.support:appcompat-v7:24.0.0'
			}

    1. ```com.android.library``` を ```com.android.model.library``` へ変更
    2. ```android``` ブロックを ```model``` で囲む
        1. ```dependencies``` は ```model``` の外
    3. ```minSdkVersion``` と ```targetSdkVersion``` に ```.apiLevel``` を追加
    4. ```android``` ブロックに ```ndk``` を追加
    5. ライブラリなので ``buildTypes``` をコメントアウト
    6. ```addAll``` ではなく ```+=``` を使うと紛らわしいエラーが出る: ```Error:Unable to load class 'com.android.build.gradle.managed.NdkConfig$Impl'.```

6. ライブラリモジュールに Java Class を追加し native メソッドを追加

		public static native String getStringFromJni();


7. 赤字になったメソッド名を選択して、左の赤い「！」から ```Create function```
    1. jni フォルダと C ソースが作成される
    2. JNI メソッドが生成されなかったので、仕方なく自分で記述

			#include <jni.h>

			JNIEXPORT jstring JNICALL
			Java_jp_kenichi_mylibrary_LibCppClass_getStringFromJni(JNIEnv *env, jobject instance) {
			   return (*env)->NewStringUTF(env, "Hello 世界");
			}

8. MainActivity で定義した native メソッドを使う
    1. ライブラリをロード

			static {
				System.loadLibrary("mylibrary");
			}

    2. layout で TextView に id を付ける
    3. その TextView に JNI から取得した文字列を設定

			((TextView)findViewById(R.id.messageTextView)).setText(jp.kenichi.mylibrary.LibClass.getStringFromJni());

9. そのまま Run したら ```couldn't find "libmylibrary.so"``` エラーが出たが Android Studio を再起動して再度 Run したら動いた

10. C を C++ に変更

  1. h

			#include <jni.h>

			extern "C" {

			JNIEXPORT jstring JNICALL
			Java_jp_kenichi_mylibrary_LibClass_getStringFromJni(JNIEnv *env, jobject instance);

			}

 2. cpp

			#include "libclass.h"

			JNIEXPORT jstring JNICALL
			Java_jp_kenichi_mylibrary_LibClass_getStringFromJni(JNIEnv *env, jobject instance) {
			   return env->NewStringUTF("Hello 世界++");
			}

# 参考

1. [gradle-experimental 0.7.0 を使う方法](http://ph0b.com/new-android-studio-ndk-support/)
2. [Android Studio で Hello-JNI を作る方法](https://codelabs.developers.google.com/codelabs/android-studio-jni/index.html)
3. Android N では [STL を 3 個の実装から選べる](http://android-developers.blogspot.jp/2016/06/improving-stability-with-private-cc.html)
    1. LLVM libc++
    2. GNU STL
    3. libstlport
    4. [各実装の説明](https://developer.android.com/ndk/guides/cpp-support.html)