AndroidStudioでNDK(JNI)を活用する 準備編


## 目的
* セキュリティの向上
 * Androidのapkパッケージは、ProGuardで難読化が行われます。しかし、難読化は暗号化ではないので、ソースコード上に、暗証番号などを記載していた場合、リバースエンジニアリングですぐに見破られてしまいます。
 * Native(C,C++)のソースコード内に暗証番号を記載し、それをJavaから呼び出すことで、隠蔽することができます。
* 高速化
 * AndroidのDalvik VM（仮想マシンで）は、VMであるがゆえに処理速度で越えられない壁が存在します。この越えられない壁を越えなければいけない状況に陥った時に、OSが直接実行できるバイトコードにできるNDKが有効です。
 * ほとんどの場合、Javaのプログラムの最適化で済むと思いますが、端末内で画像処理を行うなどの場合には、選択肢に入ってくると思います。
* iOS,Androidでのライブラリの共通化
 * C,C++でソースコードを書くことができるので、iOS(Objective-C)からも読み込むことができますし、AndroidでもJNIの型にマッチさせれば、読み込むことができます。内部ロジックはそのままで、インターフェイスの書き換え（もしくは、ラップ）のみで両OSから、利用することができます。
 * 注意点としては、環境によってバイトオーダー（ビッグエンディアン, リトルエンディアン）が異なります。


## 環境構築（2015/10/10現在）
[Android NDK Preview](http://tools.android.com/tech-docs/android-ndk-preview)
Android Studio 1.3以上、Gradle 2.5以上が必須のようです。
今回は、以下の環境で試しました。

* Android Studio 1.4
* Gradle 2.5
* Android NDK r10e

### Gradleのバージョンアップ
Android Studio 1.4で、新規プロジェクトを作成した場合、Gradleのバージョンは、2.4でした。
 1. Android Studioを起動
 2. 左メニュー（Project）の『app』を<<右クリック>>、『Open Module Settings』を選択します。
 3. 『Project』の”Gradle version”を[最新のGradle](http://services.gradle.org/distributions)にします。今回は、『2.5』と入力しました。
 4. "Gradle project sync in progress..."と表示され、自動的にバージョンアップが行われます。
 5. 5分ほどで完了します。

### Android NDKのダウンロード
 1. AndroidStudioを起動
 2. 左メニュー（Project）の『app』を<<右クリック>>、『SDK Location』を選択します。
 2. "Android SDK Location:"の下にある『Download Android NDK』をクリックします。
 3. 20分ほどで完了します。

###Experimental Pluginの適用
[Experimental Plugin User Guide](http://tools.android.com/tech-docs/new-build-system/gradle-experimental)

#### 『gradle-wrapper.properties』修正
 * 変更前
 
```./gradle/wrapper/gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip
```

 * 変更後

```./gradle/wrapper/gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip
```


#### プロジェクトの『build.gradle』修正
 * 変更前

```./build.gradle
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.3.0'
    }
}

```
 * 変更後（実験的……。まぁ、実験好きですけど）

```./build.gradle
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle-experimental:0.2.0'
    }
}

```

#### appの『build.gradle』の修正
 * 変更前
 
```./app/build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

    defaultConfig {
        applicationId "info.*****.sbx.ndk01"
        minSdkVersion 22
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

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

* 変更後
 * pluginが変わるので、そもそものDSL（記述方法）が変わります。
 * 今回は、『hello.c』というファイルを『libhello.so』というダイナミックリンクライブラリにコンパイルして、Android内から利用する想定です。

```./app/build.gradle
apply plugin: 'com.android.model.application'

model {
    android {
        compileSdkVersion = 23
        buildToolsVersion = "23.0.1"

        defaultConfig.with {
            applicationId = "info.*****.sbx.ndk01"
            minSdkVersion.apiLevel = 22
            targetSdkVersion.apiLevel = 23
        }
    }

    /*
     * native build settings
     */
    android.ndk {
        moduleName = "hello"
        /*
         * Other ndk flags configurable here are
         * cppFlags += "-fno-rtti"
         * cppFlags += "-fno-exceptions"
         * ldLibs    = ["android", "log"]
         * stl       = "system"
         */
    }
    android.buildTypes {
        release {
            minifyEnabled = false
            proguardFiles  += file('proguard-rules.txt')
        }
    }
    android.productFlavors {
        create("arm") {
            ndk.abiFilters += "armeabi"
        }
        create("arm7") {
            ndk.abiFilters += "armeabi-v7a"
        }
        create("arm8") {
            ndk.abiFilters += "arm64-v8a"
        }
        create("x86") {
            ndk.abiFilters += "x86"
        }
        create("x86-64") {
            ndk.abiFilters += "x86_64"
        }
        create("mips") {
            ndk.abiFilters += "mips"
        }
        create("mips-64") {
            ndk.abiFilters += "mips64"
        }
        // To include all cpu architectures, leaves abiFilters empty
        create("all")
    }

}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.android.support:design:23.0.1'
}
```

 * "Sync Now!" 

## JNI(Java Native Interface)で、Hello World!
### ライブラリファイルの作成
* JNI用のフォルダを作成します
 * app/src/main/jni/
* hello.cを作成します
 * ヘッダファイル（.h）は不要。 
 * この時重要なのは関数名です。これを間違えると、時間だけが浪費されます。
 * JNI(Java Native Interface)の名が示す通り、ビルド時に命名規則に従ってインターフェイスをマップしてくれます。
 * Java_{プロジェクトディレクトリ}_{呼び出し元クラス名}_{メソッド名}

```cpp:app/src/main/jni/hello.c
#include <string.h>
#include <jni.h>

jstring
Java_info_*****_sbx_ndk01_MainActivity_stringFromJNI( JNIEnv* env, jobject thiz ) {
    return (*env)->NewStringUTF(env, "Hello JNI !");
}
```

### MainActivityからの呼び出し
```java:MainActivity.java
public class MainActivity extends AppCompatActivity {

	// hello.soの読み込み
    static {
        System.loadLibrary("hello");
    }

	// helloライブラリにあるメソッドの定義
    public native String  stringFromJNI();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

		// メソッドの呼び出し
		// Hello JNI !と表示される
        Log.i("", stringFromJNI());
    }
}

```

## 参照
 * [Android NDK](http://developer.android.com/ndk/index.html)
 * [Android NDK Sample](https://github.com/googlesamples/android-ndk)

## ちょっといっぷく
* 実際のダイナミックリンクライブラリはどこにあるのか？
 * デバッグビルドだと以下のディレクトリにあるようです。
 * app/build/intermediates/binaries/debug/all/obj/armeabi-v7a/libhello.so
* Eclipse + NDK
 * 3~4年前、実際の製品を作った時には、Windows + Eclipse + NDKで、Cソースを書き換えるたびにCygwinでビルドして、その後、Eclipseでビルドして、と手間が多かったですが、AndroidStudioでは、二つのビルドを一気にやってくれます。
 * 基本的に、『.so』ファイルに変更がなければ、そのままリリースしていましたが、何かの拍子に同封し忘れて（警告はでない）すとんと落ちていたということがありました。AndroidStudioでは、ワンストップなので、きっとそんなこともなくなるでしょう。
* 使い所
 *  Googleのサンプルを見る限り、やはりマルチメディア（音声、画像、3Dモデリング）の扱いが多いようです。


## つづく
 * JNIの型とデータ構造（予定）
 * デバッグ方法（予定）
 * NDKを利用した暗証番号の隠蔽（予定）