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++ファイルを追加する。
今回は、初期化、加算、取得する簡単なサンプルを作成。
#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フォルダに作成する。
#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'**へ
・変更前
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
}
・変更後
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
色々と変えるとこがあるので下記を確認してください。
曲者は**「=」**を入れて変更する必要がでてきます。
・変更前
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'
}
・変更後
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呼び出し
//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を作成
/* 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
//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
#参考
- SWIG
SWIG and Android
Android NDK in Android Studio with SWIG
SWIG Wikipedia - NDK
Experimental Plugin User Guide - JNI
JavaからCの処理を呼ぶ方法(JNI/JNA/SWIG) - JNA
JNIより簡単にJavaとC/C++をつなぐ「JNA」とは
明日は @urapico さんの投稿となります!!お楽しみにー。