Eclipse × AndroidNDK


# 概要

いつも、ターミナルからjavahを使ってヘッダー作成＆ndk-buildコマンドを使ってnativeソースのビルドを行い、
Eclipseに戻ってプロジェクト本体のClean＆Buildしている。
↓
これをEclipse上のみで実施する方法をメモ。

# 環境

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

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

![20141123211441.jpg](https://qiita-image-store.s3.amazonaws.com/0/53393/18b489db-b022-de2f-059a-81e5f6691b76.jpeg)

# Add Native Support...

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

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

![20141123203239.jpg](https://qiita-image-store.s3.amazonaws.com/0/53393/f071b07b-18b7-3163-cc1f-bed6ea7bb6c8.jpeg)

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

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

![20141123212152.jpg](https://qiita-image-store.s3.amazonaws.com/0/53393/70586e63-3299-3738-b94b-d75dd0e54c8e.jpeg)

* jni
* jni/NDKSample.cpp
* jni/Android.mk

が自動生成されました。

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

ここは自分で作ります。

![20141123212250.jpg](https://qiita-image-store.s3.amazonaws.com/0/53393/145a6ef1-17a4-012c-e259-13044efeeeb9.jpeg)

* jp.chibi.ndksample.natives
* NativeNDKSample.java

を作成しました。

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

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

# JNIヘッダーを生成

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

![20141123212710.jpg](https://qiita-image-store.s3.amazonaws.com/0/53393/e13c7c6e-eeb3-79ea-f30d-382e22f5db93.jpeg)

* build_jni.xml

を追加しました。

```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属性で指定したものが生れる。

![20141123213209.jpg](https://qiita-image-store.s3.amazonaws.com/0/53393/385a47d6-e2c5-3d5d-8355-9ca3b7bab2ae.jpeg)

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

```cpp: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ヘッダーを実装

```cpp: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()から持って帰ってきた文字列を表示させてみます。

```java: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);
    }
```

![20141123215232.jpg](https://qiita-image-store.s3.amazonaws.com/0/53393/43070ba9-8fcb-f6e7-2775-5384232e6802.jpeg)

ちゃんとできてそうだ。

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

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

![20141123215855.jpg](https://qiita-image-store.s3.amazonaws.com/0/53393/321d62b7-2444-d31c-4ae1-c8f93d43991d.jpeg)

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

![20141123215732.jpg](https://qiita-image-store.s3.amazonaws.com/0/53393/eadb0f08-e1f3-bbe8-ab10-5d41420dbd79.jpeg)

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

![20141123215820.jpg](https://qiita-image-store.s3.amazonaws.com/0/53393/8c3ef912-20a5-a8d4-21ce-d92eaf88299c.jpeg)

だいたい...こんな感じかな...
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さんと仲良くなれた気がした。