Android NDK では次の両方ができます。
- Java側からC/C++関数を呼び出す
- C/C++側からJavaメソッド呼び出し
前者はググればやり方がたくさん出てきます。
ただ後者の情報が分かりにくい...
それならばということで、
C/C++側からJavaメソッドを呼出方法 をまとめました。
その手順をまとめておこうと思います。
このページの目次
手順0.Android NDKからC/C++の導入手順
ここでは省略。
詳しくは次ページを見てください。
大まかな手順だけなら こういう感じです。
- C/C++ を既存プロジェクトに追加
- CMakeLists.txtにビルド設定を追加
- モジュール側のbuild.gradleを編集
- C/C++でネイティブメソッドを定義
- Javaからネイティブメソッド呼び出し
手順だけだとスゴク複雑に感じる・・・
でも基本手順さえ押さえとけば難しくありません。
手順1.C/C++から呼びだすJavaメソッドを定義
ここでは次の MainActivity を用意しました。
▼ MainActivityの簡易的コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public class MainActivity extends AppCompatActivity { private WebView webView = null; public static final String TAG = MainActivity.class.getName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); /// ネイティブメソッド呼び出し sendNameToJni("Pisuke"); } /** C/C++から呼ばれるJavaメソッド **/ public String receiveMessageFromJni(String msg){ Log.d(TAG, "Received : " + msg); return msg; } /** * C++のネイティブメソッドを宣言 * Java_ とパッケージを除外した名前にすべし **/ public native String sendNameToJni(String name); /** C/C++ライブラリを読み込み **/ static { System.loadLibrary("app"); } } |
ここには次2つのメソッドがあります。
- sendNameToJni
C++側で定義されているネイティブメソッド。この関数はString型の引数1つ持ち、String型の返り値をかえす。それからネイティブメソッドにはnative装飾子も必要。
- receiveFromJni
C++側から呼びだしされるMainActivityのJavaメソッド。 sendNameToJni の内部で呼ばれるような使い方をされる(後述)
MainActivity側はこういうコード
ここまでは特殊な手順は必要ありません。
手順2.C/C++コードからJavaメソッド実行
ここでは次の条件でネイティブメソッド定義しました。
- ソースは app.cpp のファイル名
- コード・関数定義にはC++を使用
- 外部ライブラリは一切使わない
ここでは std::string など使うのでC++導入済み。
ただしC++導入は必須ではありません。(C言語でもOK)
この条件で次のネイティブメソッドを定義しました。
▼ app.cppにて次の関数を定義
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#include <stdio.h> #include <iostream> #include <stdlib.h> #include <string.h> #include <jni.h> extern "C" JNIEXPORT jstring JNICALL Java_com_examplea_example_MainActivity_sendNameToJni( JNIEnv* env, jobject thiz, jstring j_name ) { /// MainActivityのメソッドに渡す引数生成 std::string name = env->GetStringUTFChars(j_name, 0); std::string msg = "Hello "; msg += name; msg += " from JNI!!"; /// MainActivityのメソッド呼び出し jstring j_msg = (*env).NewStringUTF(msg.c_str()); jclass clazz = (*env).FindClass( "com/example/example/MainActivity"); jmethodID jmid_receiveFromJni = (*env).GetMethodID( clazz, "receiveMessageFromJni", "(Ljava/lang/String;)Ljava/lang/String;"); jstring jstr_result = (jstring)( (*env).CallObjectMethod(thiz, jmid_receiveFromJni, j_msg)); /// 返り値 std::string result= (*env).GetStringUTFChars(jstr_result, NULL); return (*env).NewStringUTF(result.c_str()); } |
大事なのはハイライトした部分。
大まかな流れとしては 1. (*end).FindClass からMainActivityクラスを取得 => 2. (*env).GetMethodId にクラス・メソッド名・返り値と引数の型を渡してメソッドID取得 => 3. (*env).CallObjectMethod からJavaメソッド呼び出し。
こうなってます。少し面倒かもしれない。
ただ基本手順さえ分かれば難しくないです。
あと次のポイントにも要注意ですね。
- Cとしてexportすること
- 関数名の命名規則
この2つは必ず守ってください。(特にC++)
C/C++からのJavaメソッドの呼出手順まとめ
簡単にここまでの流れをまとめ
以上、NDK C/C++からJavaメソッド呼出しでした。
間違いがあればご指摘ください。ではまた($・・)/~~~