LoginSignup
11
13

More than 5 years have passed since last update.

JNI JNA SWIG for Android

Posted at

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 さんの投稿となります!!お楽しみにー。

11
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
13