JNI JNA SWIG for Android


![advent_calender_17.png](https://qiita-image-store.s3.amazonaws.com/0/58514/80bef836-c32f-d897-aa61-c749358c02e9.png)

Goodpatch Advent Calendar 17日目の投稿です。
こんにちは。@hi6484です。
Goodpatchで日々Androidアプリを作ってます。

昨日は @ghostnotesさんの [[Google I/O 2015 Android Appのソースを読んでみた。](http://qiita.com/ghostnotes/items/b04e614cc6acc0ca0c36)」でした。
二日連続のAndroid関連の記事となります。

あるアプリにて、C言語で書かれたモジュールを使う事になったため、
JNI,JNA,SWIGとかを色々と調べてみたので、その事に付いて書いてます。

android studioもNDK対応してくれたので、以前に比べるとかなり楽に実装できるようになってます。
(以前はsoファイルを作るためのAndroid.mkとか環境構築とか大変だった・・・)

JNAは実装できなかったんですが、
そもそもJNIに比べて速度が遅いみたいなんで、やらなくていいんじゃないか。
メンテも２年前くらいから止まってるし（言い訳です）

色々と調べていて、SWIGを見つけました。
SWIGはJNIのダルい部分を省いてくれるので素敵な感じです(中身はJNIです)


#JNI-Java Native Interface-
##C/C++ファイルの追加
jniフォルダを作成して、C/C++ファイルを追加する。
今回は、初期化、加算、取得する簡単なサンプルを作成。

```c:sample_advent.c
#include "sample_advent.h"

uint32_t dataVaule;

void adventInitValue(void) {
    dataVaule = 0;
}

void adventAddValue(uint32_t value) {
    dataVaule += value;
}

uint32_t adventGetValue(void) {
    return dataVaule;
}
```
**sample_advent.h**も同様に作成。

##jniファイルの追加
先ほど作成したjniフォルダに作成する。

```c:jni_sample_advent.c
#include <string.h>
#include <jni.h>
#include "sample_advent.h"


void Java_jp_hi6484_android_sample_adventcalendarjni_MainActivity_adventInitValue (JNIEnv* env,
                                                                                   jobject thiz) {
    adventInitValue();
}

void Java_jp_hi6484_android_sample_adventcalendarjni_MainActivity_adventAddValue (JNIEnv* env,
                                                                                    jobject thiz,
                                                                                    uint32_t value) {
    adventAddValue(value);
}

uint32_t Java_jp_hi6484_android_sample_adventcalendarjni_MainActivity_adventGetValue (JNIEnv* env,
                                                                                     jobject thiz) {
    return adventGetValue();
}
```

##build.gradle変更

###Project/build.gradle
classpathの下記の箇所を変更
'com.android.tools.build:gradle:1.5.0'
　→**'com.android.tools.build:gradle-experimental:0.4.0'**へ
**・変更前**

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

        // 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
}
```

**・変更後**

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

        // 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
}

```

###app/build.gradle
色々と変えるとこがあるので下記を確認してください。
曲者は**「=」**を入れて変更する必要がでてきます。

**・変更前**

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

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        applicationId "jp.hi6484.android.sample.adventcalendarjni"
        minSdkVersion 19
        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.1.1'
    compile 'com.android.support:design:23.1.1'
}
```

**・変更後**

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

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

        defaultConfig.with {
            applicationId = "jp.hi6484.android.sample.adventcalendarjni"
            minSdkVersion.apiLevel = 19
            targetSdkVersion.apiLevel = 23
            versionCode = 1
            versionName = "1.0"
        }
    }
    android.ndk {
            moduleName = "SampleAdventCalendar"
        /*
         * Other ndk flags configurable here are
         * cppFlags += "-fno-rtti"
         * cppFlags += "-fno-exceptions"
         * ldLibs    = ["android", "log"]
         * stl       = "system"
         */
    }

    android.buildTypes {
        release {
            minifyEnabled = false
            proguardFiles.add(file('proguard-rules.txt'))
        }
    }

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

##JavaからNative呼び出し

```java:MainActivity.java

    //libSampleAdventCalendar.soファイルの読み込み.
    static {
        System.loadLibrary("SampleAdventCalendar");
    }

    //nativeメソッドの作成.
    public native void adventInitValue();
    public native void adventAddValue(int value);
    public native int adventGetValue();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        //初期化.
        adventInitValue();
        //加算.
        adventAddValue(100);
        //取得.
        int value = adventGetValue();

    }
```

**libSampleAdventCalendar.so**は下記フォルダに作成される
**[app/build/intermediates/binaries/debug/all]**
libSampleAdventCalendar.soの読み込みに失敗している場合などは上記フォルダにsoファイルが存在するか確認するとよい。

##JNIの面倒な点
JAVAとC/C++を繋ぐJNIを書くのが面倒。
パッケージ名を含めた関数名って・・・。
Java_jp_hi6484_android_sample_adventcalendarjni_MainActivity_adventGetValue
1文字間違えてもだめ！！


#SWIG-Simplified Wrapper and Interface Generator-

##SWIGインストール
ターミナルから下記を叩けばOK

```
brew install https://raw.github.com/cobrateam/formulae/master/swig.rb
```

##C/C++ファイルの作成
上記、JNI時と同じ。

##.iファイルの作成
jniフォルダ以下に**sample_advent.i**を作成

```sample_advent.i
/* File : sample_advent */
%module sample_advent
%include "stdint.i"

%inline %{
    extern void adventInitValue(void);
    extern void adventAddValue(uint32_t value);
    extern uint32_t adventGetValue(void);
    %}
```

実際、.iファイルってよくわからない・・・
だれかわかる方or参考サイト知っている方教えてください。
個人的には.hファイルをexternを前に付けて作成しております。

##JNIファイルの作成
ターミナルで[app/src/main]まで移動して
下記のコマンドを実行

```
swig -java -package jp.hi6484.android.sample.adventcalendarswig jni/sample_advent.i
```

実行するとjniフォルダ直下に下記３ファイルが自動生成される
**sample_advent_wrap.c**
**sample_advent.java**
**sample_adventJNI.java**
※javaファイルは後ほど移動。

##build.gradle変更
上記、JNI時と同じ。

##JavaからNative呼び出し
先ほど自動生成された下記2ファイルをjavaフォルダに移動.
**sample_advent.java**
**sample_adventJNI.java**


```java:MainActivity.java

    //libSampleAdventCalendarSWIG.soファイルの読み込み.
    static {
        System.loadLibrary("SampleAdventCalendarSWIG");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        //初期化.
        sample_advent.adventInitValue();
        //加算.
        sample_advent.adventAddValue(100);
        //取得.
        int value = sample_advent.adventGetValue();

    }
```

sample_advent.javaにnativeメソッドは作成されているので呼び出すだけ

##SWIGの面倒な点
- .iファイルがよくわからない。
- けれども、jniファイルは自動生成で吐き出されるため楽。

#Sample Code
[SampleAdventCalendarSWIG](https://github.com/hiroshi-hashimoto/SampleAdventCalendarSWIG)
[SampleAdventCalendarJNI](https://github.com/hiroshi-hashimoto/SampleAdventCalendarJNI)

#参考
- SWIG  
[SWIG and Android](http://www.swig.org/Doc2.0/Android.html)
[Android NDK in Android Studio with SWIG](http://www.sureshjoshi.com/mobile/android-ndk-in-android-studio-with-swig/)
[SWIG Wikipedia](https://ja.wikipedia.org/wiki/SWIG)
- NDK  
[Experimental Plugin User Guide](http://tools.android.com/tech-docs/new-build-system/gradle-experimental)
- JNI  
[JavaからCの処理を呼ぶ方法(JNI/JNA/SWIG)](http://qiita.com/kiida/items/9d26b850194fa1a02e67)
- JNA  
[JNIより簡単にJavaとC/C++をつなぐ「JNA」とは](http://www.atmarkit.co.jp/fjava/special/jna/jna_1.html)

明日は @urapico さんの投稿となります！！お楽しみにー。