読者です 読者をやめる 読者になる 読者になる

DRY

Web関連の技術の事、食事/お酒の事、旅行の事など

EclipseCDTとandroid-ndk-r6のJNIをプロジェクト統合する方法

Android

別に結果としてやらなくても良かった気がしますが、まあ1つのAndroidアプリとしてJNIも梱包した形が理想なのでEclipseも統合しておこうかなと。

# 環境
Windows7
Eclipse 3.6 Helios
Cygwin
android-ndk-r6


とりあえず、JNI作ってndk-buildで単体では動いている事とCDTはインストール済みと言う事を前提とします。
そちらのやり方は以前のエントリを見て下さい。



まずはプロジェクトの原型です(新たに今回用に「IntegrateJni」というプロジェクトを作りました)
現状は単なるAndroidプロジェクトです。
JavaとJNIでそれぞれ100000000回ループを入れて、掛かった時間を計測しLog.dするという単純なものです。

それぞれのファイルの中身は以下のようになっています。


●IntegrateJni.java


package jp.k16.integratejni;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;


public class IntegrateJni extends Activity
{
static
{
System.loadLibrary ("calculate");
};

public native long calculate ();

@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

calc_java ();
calc_native ();
}

private void calc_java ()
{
long start = System.currentTimeMillis ();
long count = 0;
for(int i = 0; i < 100000000;i++)
{
count++;
}
long stop = System.currentTimeMillis ();
Log.d ("TAG", "calc_java = " + String.valueOf (stop - start));
}

private void calc_native ()
{
long start = System.currentTimeMillis ();
calculate ();
long stop = System.currentTimeMillis ();
Log.d ("TAG", "calc_native = " + String.valueOf (stop - start));
}
}


●jp_k16_integratejni_IntegrateJni.h


/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class jp_k16_integratejni_IntegrateJni */

#ifndef _Included_jp_k16_integratejni_IntegrateJni
#define _Included_jp_k16_integratejni_IntegrateJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: jp_k16_integratejni_IntegrateJni
* Method: calculate
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_jp_k16_integratejni_IntegrateJni_calculate
(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif


●jp_k16_integratejni_IntegrateJni.cpp


#include "jp_k16_integratejni_IntegrateJni.h"


JNIEXPORT jlong JNICALL Java_jp_k16_integratejni_IntegrateJni_calculate (JNIEnv *, jobject)
{
long count = 0;
for (int i = 0; i < 100000000; i++)
count++;

return count;
}


Android.mk


LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := calculate
LOCAL_SRC_FILES := jp_k16_integratejni_IntegrateJni.cpp

include $(BUILD_SHARED_LIBRARY)


まずはこのプロジェクトを「Convert to C/C++ Project」します。
「プロジェクトを右クリック」⇒「新規」⇒「その他」を選択するとダイアログが立ち上がるので、ここで「Convert to C/C++ Project」を選択し次へ進みます。

そうすると「C/C++プロジェクトの変換」ダイアログが出るので、「プロジェクトタイプをMakefileプロジェクト」に、「ツールチェーンを他のルーツチェーン」に指定して完了します。

パークティブを開いてOKを押すと、下記のような画面に戻ります。

ここで、ビルドに必要な「libs」と「obj」フォルダをsrcなどと同じ階層に作っておきます。
(ndk-buildしても構いません。既に存在している場合もあると思います。)


プロジェクトを右クリックしてプロパティを見ると、きちんと「C/C++ビルド」と「C/C++一般」が追加されている事を確認します。

まずは「C/C++一般」⇒「パス及びシンボル」⇒「インクルード」タブにてインクルードディレクトリを指定してあげます。
※今回は無駄にC++にしたので「GNUC++」の設定になります。
私のNDKのルートは前回のOpenSSLのエントリ時にC:\ndkになっているので、こちらに通します。
C:\ndk\platforms\android-8\arch-arm\usr\include
※各AndroidAPIに見合ったincludeファイルを指定して下さい。


「ソースロケーション」タブではJNIのファイルがある「jni」フォルダを指定して下さい。


「出力ロケーション」タブでは「libs」「obj」の二つを指定して下さい。


これで「適用」ボタンを押すと、リビルドしますか?的なダイアログが出ますが、これはNOで終わらせて下さい。


次に「C/C++ビルド」⇒「環境」にPATHを付け足します。


名前を「PATH」で入れて下さい。

そうするとこういう設定になっているはずです。



最後に「ビルダー」の設定をします。
Windows + Cygwinで行っている場合、これがミソになると思います。

まずはプロジェクトを右クリックして「ビルダー」を選択します。


「新規」ボタンを押下し、「プログラム」を選択します。


名前は私は「NDKBuilder」としました。
ロケーションに「ファイル・システムの参照」ボタンよりCygwinbash.exe」を指定します。
作業ディレクトリーは「ワークスペースの参照」ボタンより今回のプロジェクトを指定します。
引数に実際に起動させたいNDKのコマンドを打ち込みます。「C:\ndk\ndk-build -B NDK_DEBUG=1」
※実はこの後gdbを使ってJNIのデバックがしたいので、「NDK_DEBUG=1」を先に指定しておきます。オプションを入れておかないとgdbが立ち上がらないので。。。


これで「ビルダー」の設定は終わりで、後は「C/C++ビルド」の「ビルダー設定」タブの「デフォルト・ビルド・コマンドを使用」のチェックを外し、「ビルド・コマンド」を空にします。



これで設定はすべて終わりです。「適用」ボタンを押してプロジェクト右クリックから「Androidアプリ」を開始してみて下さい。
ちなみに、Eclipseのメイン画面に戻ってきたら、自動でコンパイルが始まって下記のように「libs」と「obj」フォルダに.soファイルなどが出来ているはずです。


後はブレークポイントを張って貰ってもよし、DDMSの画面を開いてしばらく待って貰っても良しですが結果が得られるはずです。


やっぱりJNIの方が速いですね。



なお最後ですが、今回はこちらのサイトを、若干EclipseやNDKのバージョンが違ったりしますがかなり活用させて頂きました。
またもや勝手ならがリンクさせて頂きます。