cocos2d-x JNI サンプル集

この投稿は[モバイルファクトリー Advent Calenader](http://qiita.com/advent-calendar/2015/mobilefactory)の20日目のものです。


この記事ではcocos2d-x 3系で、何かと面倒で忘れがちなJNI記法・使用例を記述していきます。

## JNIとは

> Java Native Interface (JNI) は、Javaプラットフォームにおいて、Javaで記述されたプログラムと、他の言語（たとえばCやC++など）で書かれた、実際のCPUの上で動作するコード（ネイティブコード）とを連携するためのインタフェース仕様である。

> [wikipedia](https://ja.wikipedia.org/wiki/Java_Native_Interface)より引用

## メソッドシグネチャ

### シグネチャとは

引数の型、引数の数、メソッド名の組み合わせを組み合わせをシグネチャ（シグニチャ）と呼びます。

### 型対応表
| java | 型のシグネチャ |
|:--|:--:|
|void | V |
|byte | B |
|boolean | Z |
|char | C | 
|short | S |
|int | I |
|long | J |
|double	 | D |
|float | F |
|String	 | Ljava/lang/String; |
|その他javaクラス | L + パッケージ/クラス名 + ; |

### メソッドシグネチャ例
| c++ | シグネチャ例 |
|:--|:--:|
| void hoge(); | "()V" | 
| void hoge(int a); | "(I)V" | 
| int hoge(int a) | "(I)I" |
| int hoge(int a, int b) | "(II)I" |
| long hoge(short a, double b) | "(SD)J" |
| boolean hoge(String a);	 | "(Ljava/lang/String;)Z" | 
| boolean hoge(int a[], char b, byte c[] ) | "([ab[c)Z" |

### 記法

`"(引数)返り値"`

- 引数がない場合未指定
- 引数を複数持たせる場合、連続してシグネチャを記述する
- 配列はシグネチャの前に`[`を記述する

より詳細な情報は[OracleのJNI仕様](https://docs.oracle.com/javase/jp/1.5.0/guide/jni/spec/types.html)などを見ると良いかと思います。

JNIの記述例は下記サイトが解りやすくまとまっています。
[JNIサンプル集 : 技術者のたまごブログ](http://setohide.blogspot.jp/2014/01/jni.html)


## cocos2d::JniHelper

cocos2d::JniHelperを利用した記述例を記載していきます。

### 記法

JniHelperの[ドキュメント](http://www.cocos2d-x.org/reference/native-cpp/V3.9/index.html)が見当たらなかったのですが、基本的には[JNIの関数](https://docs.oracle.com/javase/jp/1.5.0/guide/jni/spec/functions.html)なので気にしないことにします。

```

cocos2d::JniMethodInfo methodInfo;
if (cocos2d::JniHelper::getStaticMethodInfo(methodInfo, "名前空間/クラス名", "関数名", "メソッドシグネチャ")) 
{
    // メソッド呼び出し
     methodInfo.env->CallxxxMethod...
   
    // 開放処理
    methodInfo.env->DeleteLocalRef(methodInfo.classID);
}
```

インスタンスメソッドも呼べると思うのですが、どうもうまく叩けず...
今回はjava側がstaticメソッドとしたサンプルを作成します。

### cocos2d::JniHelper使用例

- void hoge();

```cpp
cocos2d::JniMethodInfo methodInfo;
if (cocos2d::JniHelper::getStaticMethodInfo(methodInfo, "JAVA_CLASS_NAME", "hoge", "()V")) 
{
    methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID);
    methodInfo.env->DeleteLocalRef(methodInfo.classID);
}
```

- void hoge(int a, int b);

```cpp
cocos2d::JniMethodInfo methodInfo;
if (cocos2d::JniHelper::getStaticMethodInfo(methodInfo, "JAVA_CLASS_NAME", "hoge", "(II)V")) 
{
    methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, 1, 2);
    methodInfo.env->DeleteLocalRef(methodInfo.classID);
}
```

- String hoge();

```cpp
std::string ret;

cocos2d::JniMethodInfo methodInfo;
if (cocos2d::JniHelper::getStaticMethodInfo(methodInfo, "JAVA_CLASS_NAME", "hoge", "()Ljava/lang/String;")) 
{
    jobject objResult = methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.methodID);
    ret = cocos2d::JniHelper::jstring2string((jstring)objResult); // jstringをstd::stringに変換
    methodInfo.env->DeleteLocalRef(objResult);
    methodInfo.env->DeleteLocalRef(methodInfo.classID);
}

return ret;
```
実際には下記のようにプラットフォーム別に処理を分けて書くと思います。

```
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    // jni
#elif(CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    // obj-c++
#endif
```
[他プラットフォーム](https://github.com/cocos2d/cocos2d-x/blob/0abe235323aaf03f457d5fa395ded1cd38c43713/cocos/platform/CCPlatformConfig.h#L41-L54)


### おわりに

JNI周りはエラーが追いづらいので、ある程度まではコピペ生活が出来ると幸せかなと思っています。
今後も使用例は更新していきます。（サンプル集というには少ないので...

明日は[morigamix](http://qiita.com/morigamix)さんです！