Eclipse × AndroidNDK


概要

いつも、ターミナルからjavahを使ってヘッダー作成&ndk-buildコマンドを使ってnativeソースのビルドを行い、
Eclipseに戻ってプロジェクト本体のClean&Buildしている。

これをEclipse上のみで実施する方法をメモ。

環境

  • Ubuntu 14.04
  • Android SDK (SDK Managerで最新まで更新)
  • Android NDK r9b
  • Eclipse Luna

とりあえずデフォルト状態のプロジェクト

Add Native Support...

AndroidプロジェクトにNative用の設定を追加します。

  • プロジェクトを右クリック -> Android Tools -> Add Native Support

※ このようにエラーが出ている場合は、Android NDKのパスが設定されていません。
※ メニューバー -> Window -> preference -> Android -> NDK から設定してください。

Add Native Support を実行した後のプロジェクト

  • jni
  • jni/NDKSample.cpp
  • jni/Android.mk

が自動生成されました。

nativeメソッドを持つクラスを作成

ここは自分で作ります。

  • jp.chibi.ndksample.natives
  • NativeNDKSample.java

を作成しました。

NativeNDKSample.java
package jp.chibi.ndksample.natives;

public class NativeNDKSample {
    public static native String nativeGetHello();
}

JNIヘッダーを生成

JNIヘッダーを生成するためのjavahを実行する Ant Buildファイル を作成します。

  • build_jni.xml

を追加しました。

build_jni.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="build_jni" default="create_jni_header" basedir=".">
    <target name="create_jni_header">
        <javah classpath="bin/classes" class="jp.chibi.ndksample.natives.NativeNDKSample" outputfile="jni/NDKSample.h"/>
    </target>
</project>

javahタスクを使って、jni用のC/C++ヘッダーを出力します。
今回は先ほど自動生成されたjniソースファイルに合わせて NDKSample.h としました。

属性 説明 javahコマンド
classpath .classがあるディレクトリ -classpath ディレクトリ名
class 生成元となるクラス名(パッケージを含む) クラス名
destdir 生成先のディレクトリ名 -d ディレクトリ名
outputfile 生成先のファイル名 -o ファイル名

※ destdir と outputfile はどちらか一方しか宣言できない。
※ ここでは outputfile のみ使用している
※ destdir で指定した場合は jp_chibi_ndksample_natives_NativeNDKSample.h のようなヘッダーファイルが出力される。

build_jni.xml を実行

(後述でプロジェクトビルド時に一緒にビルドされるようにします)

  1. Androidプロジェクトを1回ビルドして.classを作成しておく。
  2. build_jni.xml を右クリック -> Ran As -> Ant Build
  3. 成功すると以下のようにoutputfile属性で指定したものが生れる。

javahを実行しただけなので、お馴染みのJNI用ヘッダー君です。

jni/NDKSample.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class jp_chibi_ndksample_natives_NativeNDKSample */

#ifndef _Included_jp_chibi_ndksample_natives_NativeNDKSample
#define _Included_jp_chibi_ndksample_natives_NativeNDKSample
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     jp_chibi_ndksample_natives_NativeNDKSample
 * Method:    nativeGetHello
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_jp_chibi_ndksample_natives_NativeNDKSample_nativeGetHello
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

生成されたJNIヘッダーを実装

NDKSample.cpp
#include "NDKSample.h"

/*
 * Class:     jp_chibi_ndksample_natives_NativeNDKSample
 * Method:    nativeGetHello
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_jp_chibi_ndksample_natives_NativeNDKSample_nativeGetHello
(JNIEnv* env, jclass clazz) {
    return env->NewStringUTF("NDKSample::nativeGetHello!!");
}

static nativeメソッドなので第2引数がjclassでした。

Androidプロジェクトを実装&実行

デフォルトのレイアウトにあるTextViewにIDを付けてあげて...
nativeGetHello()から持って帰ってきた文字列を表示させてみます。

MainActivity.java
    static {
        System.loadLibrary("NDKSample");
    }

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

        String str = NativeNDKSample.nativeGetHello();
        ((TextView)findViewById(R.id.hello)).setText(str);
    }

ちゃんとできてそうだ。

Ant Buildをプロジェクトビルド時に一緒に実行

  • プロジェクトを右クリック -> Properties -> Builders -> New -> Ant Builder

  • Mainタブ -> Buildfile -> Browse Workspace... -> build_jni.xmlを指定

  • Build Optionsタブ -> Specify working set of relevent resources -> NativeNDKSample.Javaを指定

だいたい...こんな感じかな...
Buildersの順番は...

  1. Android Build
  2. Ant Build
  3. ndk-build

にしたいから、
CDT Builderを↓のほうに移動して

  1. Android Package Builder
  2. New Builder(作った奴)
  3. CDT Builder

の順番にすればいいのかな?

ちなみに、
Specify working set of relevent resources を指定したことによって
NativeNDKSample.javaに更新があった際はbuild_jni.xmlが実行されるようになります。

まとめ

少しだけEclipseさんと仲良くなれた気がした。