メソッドを呼び出す

ネイティブコードからクラスやオブジェクトのメソッドを呼び出す.
流れ
サンプル
インスタンスメソッドとスタティックメソッド
メソッドIDの取得とシグネチャ
javap

流れ

メソッドを呼び出す時の流れは,フィールドの取得や設定とそんなに変わらない.以下にその流れを示す.

インスタンスメソッドの場合スタティックメソッドの場合
必要があればGetObjectClassでjclassを取得する
GetMethodIDでメソッドIDを取得GetStaticMethodIDでメソッドIDを取得
Call<Type>Methodでメソッドを呼び出すCallStatic<Type>Methodでメソッドを呼び出す

<Type>にはメソッドの返り値の型と対応するInt,Short,Byteなどが入る.

サンプル

流れが掴めたところでサンプルを示す.まずはネイティブコードから呼び出されるメソッドを保持するクラス.

public class Methods{
    public void publicVoidMethod(){
	System.out.println("publicVoidMethod()");
    }

    static int pkgPrivateStaticIntMethod(){
	int ret = 1;
	System.out.println("pkgPrivateIntMethod()");
	System.out.println("return " + ret);
	return ret;
    }

    protected char protectedCharMethod(String str){
	char ret = str.charAt(0);
	System.out.println("protectedCharMethod(" + str + ")");
	System.out.println("return " + ret);
	return ret;
    }

    private static double privateStaticDoubleMethod(float val){
	double ret = (double)val * val;
	System.out.println("privateDoubleMethod("+ val + ")");
	System.out.println("return " + ret);
	return ret;
    }

}

ここにはスタティックメソッドとインスタンスメソッド,そしていろんなアクセス修飾子を混ぜてみた.次にこのクラスのメソッドをネイティブコードを介して呼び出すクラス.

public class Caller{
  static{
    System.loadLibrary("Caller");
  }
  
  native void callPublicVoidMethod(Methods methods);
  native int callPkgPrivateStaticIntMethod(Methods methods);
  native char callProtectedCharMethod(Methods methods, String str);
  native double callPrivateStaticDoubleMethod(Methods methods, float val);
  
  public static void main(String args[]){
    Caller caller = new Caller();
    Methods methods = new Methods();
    
    caller.callPublicVoidMethod(methods);
    caller.callPkgPrivateStaticIntMethod(methods);
    caller.callProtectedCharMethod(methods, "hoge");
    caller.callPrivateStaticDoubleMethod(methods, 1.0f);
  }
}

そしてネイティブメソッド.まずヘッダ.

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

#ifndef _Included_Caller
#define _Included_Caller
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Caller
 * Method:    callPublicVoidMethod
 * Signature: (LMethods;)V
 */
JNIEXPORT void JNICALL Java_Caller_callPublicVoidMethod
  (JNIEnv *, jobject, jobject);

/*
 * Class:     Caller
 * Method:    callPkgPrivateStaticIntMethod
 * Signature: (LMethods;)I
 */
JNIEXPORT jint JNICALL Java_Caller_callPkgPrivateStaticIntMethod
  (JNIEnv *, jobject, jobject);

/*
 * Class:     Caller
 * Method:    callProtectedCharMethod
 * Signature: (LMethods;Ljava/lang/String;)C
 */
JNIEXPORT jchar JNICALL Java_Caller_callProtectedCharMethod
  (JNIEnv *, jobject, jobject, jstring);

/*
 * Class:     Caller
 * Method:    callPrivateStaticDoubleMethod
 * Signature: (LMethods;F)D
 */
JNIEXPORT jdouble JNICALL Java_Caller_callPrivateStaticDoubleMethod
  (JNIEnv *, jobject, jobject, jfloat);

#ifdef __cplusplus
}
#endif
#endif

そして本体.

#include "Caller.h"

JNIEXPORT void JNICALL Java_Caller_callPublicVoidMethod
(JNIEnv *env, jobject this, jobject methods){
  /* jclass��������� */
  jclass clazz = (*env)->GetObjectClass(env, methods);
  /* �᥽�å�ID��������� */
  jmethodID mid = (*env)->GetMethodID(env, clazz, "publicVoidMethod", "()V");
  /* �᥽�åɤ�ƤӽФ� */
  (*env)->CallVoidMethod(env, methods, mid, NULL);
}

JNIEXPORT jint JNICALL Java_Caller_callPkgPrivateStaticIntMethod
(JNIEnv *env, jobject this, jobject methods){
  jclass clazz = (*env)->GetObjectClass(env, methods);
  /* �����ƥ��å��᥽�åɤΥ᥽�å�ID����� */
  jmethodID mid = (*env)->GetStaticMethodID(env, clazz, "pkgPrivateStaticIntMethod", "()I");
  /* �����ƥ��å��᥽�åɤ�ƤӽФ� */
  return (*env)->CallStaticIntMethod(env, clazz, mid, NULL);
}

JNIEXPORT jchar JNICALL Java_Caller_callProtectedCharMethod
(JNIEnv *env, jobject this, jobject methods, jstring str){
  jclass clazz = (*env)->GetObjectClass(env, methods);
  jmethodID mid = (*env)->GetMethodID(env, clazz, "protectedCharMethod", "(Ljava/lang/String;)C");
  return (*env)->CallCharMethod(env, methods, mid, str);
}

JNIEXPORT jdouble JNICALL Java_Caller_callPrivateStaticDoubleMethod
(JNIEnv *env, jobject this, jobject methods, jfloat fval){
  jclass clazz = (*env)->GetObjectClass(env, methods);
  jmethodID mid = (*env)->GetStaticMethodID(env, clazz, "privateStaticDoubleMethod", "(F)D");
  return (*env)->CallStaticDoubleMethod(env, clazz, mid, fval);
}

以下実行結果.
publicVoidMethod()
pkgPrivateIntMethod()
return 1
protectedCharMethod(hoge)
return h
privateDoubleMethod(1.0)
return 1.0
フィールドの場合と同様,privateなメソッドを呼び出したりもできる.

インスタンスメソッドとスタティックメソッド

サンプルのネイティブコードからわかるように,インスタンスメソッドの場合は,
jmethodID mid = (env)->GetMethodID(env, clazz, “publicVoidMethod”, “()V”); (env)->CallVoidMethod(env, methods, mid, NULL);
としてメソッドIDを取得したりそのメソッドを呼び出したりした.また,スタティックメソッドの場合は,
jmethodID mid = (env)->GetStaticMethodID(env, clazz, “pkgPrivateStaticIntMethod”, “()I”); (env)->CallStaticIntMethod(env, clazz, mid, NULL);
としてメソッドIDを取得したりそのメソッドを呼び出したりした.GetMethodIDやCallMethod系の関数名には,フィールドの取得や設定と同様の特徴がある.つまり,インスタンスメソッドの場合は関数名にStaticがつかなくて,スタティックメソッドの場合では関数名にStaticがつく.

メソッドIDの取得とシグネチャ

サンプルにおいてメソッドIDを取得する部分には
jmethodID mid = (*env)->GetMethodID(env, clazz, “protectedCharMethod”, “(Ljava/lang/String;)C”);
などがある.なんかかなりウザイ雰囲気である.まず,第一第二引数は例によってJNIEnvとjclassである.そして第三引数はメソッド名である.そして第四引数がメソッドのシグネチャである.前の項でも書いたが,ネイティブコードでのシグネチャはJavaでいうシグネチャとは異なる.ネイティブコードでのシグネチャは
(引数の型の並び)返り値の型
で表す.すなわちメソッドの名前はシグネチャに含まれない.シグネチャは前の項で示したシグネチャ一覧を使用すると解読できる.

javap

すでにネイティブに書かれているシグネチャを解読できたとしても,何もないところからそれを書くのは憂鬱である.引数の数が増えたり配列が絡んだりするとホントやってられない.しかも一番問題なのはコンパイラチェックが入らないことである(これはJNIのいろんなところで問題なのだが…).そんな問題に対処すべく,javapなるコマンドがある.上のサンプルもjavapのお世話になった.
$ javap -s -private Methods
Compiled from Methods.java
public class Methods extends java.lang.Object {
public Methods();
/* ()V / public void publicVoidMethod(); / ()V / static int pkgPrivateStaticIntMethod(); / ()I / protected char protectedCharMethod(java.lang.String); / (Ljava/lang/String;)C / private static double privateStaticDoubleMethod(float); / (F)D */
}
メソッドの宣言の次の行のコメント内がそのメソッドのシグネチャである.シグネチャの取得に必要なjavapのいくつかのオプションを以下に示す.
-s
ネイティブコードにコピペできる形式のシグネチャを出力する.
-public
publicなクラスとメンバを表示.
-protected
publicかprotectedなクラスとメンバを表示.
-package
publicかprotectedかパッケージプライベートなクラスとメンバを表示.
-private
すべてのクラスとメンバを表示.

This article was written by Fujiko