Posted at
GoodpatchDay 17

JNI JNA SWIG for Android

More than 3 years have passed since last update.

advent_calender_17.png

Goodpatch Advent Calendar 17日目の投稿です。

こんにちは。@hi6484です。

Goodpatchで日々Androidアプリを作ってます。

昨日は @ghostnotesさんの [Google I/O 2015 Android Appのソースを読んでみた。」でした。

二日連続のAndroid関連の記事となります。

あるアプリにて、C言語で書かれたモジュールを使う事になったため、

JNI,JNA,SWIGとかを色々と調べてみたので、その事に付いて書いてます。

android studioもNDK対応してくれたので、以前に比べるとかなり楽に実装できるようになってます。

(以前はsoファイルを作るためのAndroid.mkとか環境構築とか大変だった・・・)

JNAは実装できなかったんですが、

そもそもJNIに比べて速度が遅いみたいなんで、やらなくていいんじゃないか。

メンテも2年前くらいから止まってるし(言い訳です)

色々と調べていて、SWIGを見つけました。

SWIGはJNIのダルい部分を省いてくれるので素敵な感じです(中身はJNIです)


JNI-Java Native Interface-


C/C++ファイルの追加

jniフォルダを作成して、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フォルダに作成する。


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呼び出し


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フォルダ直下に下記3ファイルが自動生成される

sample_advent_wrap.c

sample_advent.java

sample_adventJNI.java

※javaファイルは後ほど移動。


build.gradle変更

上記、JNI時と同じ。


JavaからNative呼び出し

先ほど自動生成された下記2ファイルをjavaフォルダに移動.

sample_advent.java

sample_adventJNI.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

SampleAdventCalendarJNI


参考

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