JNIイニシャル--CPPでJavaオブジェクトを作成してアクセス


説明:
このインスタンスコードは、「Androidフレームワークの秘密」から来ています.
プログラム実行環境--Ubuntu 12.04 64 bit,JDK 1.6, g++ 4.6.3 .
1 javaファイルの作成
/**
 *  C   java  ,    java     
 */
public class JniFuncMain{
	
	private static int staticIntField = 300;

	static{
		System.loadLibrary("jnifunc");	
	}

	public static native JniTest createJniObject();
	
	public static void main(String[] args){
		System.out.println("java createJniObject()      ");
		
		JniTest jniObject = createJniObject();

		jniObject.callTest();  //    c   
	}
}

class JniTest{
	private int intField;

	public JniTest(int num){
		intField = num;
		System.out.println(" C       ,        ");	
	}
	
	//    JNI      
	public int callByNative(int num){
		System.out.println(" C   JniTest      callByNative. num = " + num);
		return num;	
	}

	//      C   java             
	public void callTest(){
		System.out.println(" C   java   java       ");
	}
}

2 javaファイルのコンパイル
javac JniFuncMain.java
ls JniFuncMain.class
JniFuncMain.class



3ヘッダファイルの生成
javah JniFuncMain
ls JniFuncMain.h
JniFuncMain.h

ヘッダファイルJniFuncMain.hの内容は以下の通りである.
#include <jni.h>
/* Header for class JniFuncMain */

#ifndef _Included_JniFuncMain
#define _Included_JniFuncMain
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JniFuncMain
 * Method:    createJniObject
 * Signature: ()LJniTest;
 */
JNIEXPORT jobject JNICALL Java_JniFuncMain_createJniObject
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

4 CPP実装ファイルの作成
この例では、次の点について説明します.
CPPでJavaの静的メソッドをどのように呼び出すか、
Javaオブジェクトを作成して戻す方法
Javaオブジェクトのメンバーメソッドを呼び出すには、
Javaオブジェクトのメンバー変数の変更方法
jnifunc.cppのファイル内容は以下の通りです.
#include <jni.h>
#include "JniFuncMain.h"
#include <stdio.h>

JNIEXPORT jobject JNICALL Java_JniFuncMain_createJniObject
  (JNIEnv * env, jclass clazz){

	jclass targetClass;
	jmethodID mid;
	jobject newObject;
	jstring helloStr;
	jfieldID fid;
	jint staticIntField;
	jint result;
	
	//  JniFuncMain   staticField      
	fid = env->GetStaticFieldID(clazz, "staticIntField", "I");
	staticIntField = env->GetStaticIntField(clazz, fid);
	printf("cpp  JniFuncMain   staticField      ");
	printf("    JniFuncMain.staticField = %d
", staticIntField); // targetClass = env->FindClass("JniTest"); // mid = env->GetMethodID(targetClass, "<init>", "(I)V"); // JniTest , printf("cpp niTest
"); newObject = env->NewObject(targetClass, mid, 100); // , cpp , java mid = mid = env->GetMethodID(targetClass, "callByNative", "(I)I"); // result = env->CallIntMethod(newObject, mid, 200); // JniObject intField fid = env->GetFieldID(targetClass, "intField", "I"); printf("cpp JniTest intField 200
"); env->SetIntField(newObject, fid, result); // // java , cpp , , return newObject; }

5 CPPファイルを動的ライブラリとしてコンパイル
3つの知識点に注意してください.
1)linuxの動的ライブラリはlibで始まる、libライブラリ名である必要がある.so
2)cppファイルをコンパイルlinuxのg++コマンドを使用する
3) JniFuncMain.hにはjdkの2つのヘッダファイルが含まれており、g++コマンドで2つのヘッダファイルのパスを指定する必要があります.
コンパイルコマンドは次のとおりです.
 g++ -fPIC -D_REENTRANT -I/develop/jdk1.6.0_31/include -I//develop/jdk1.6.0_31/include/linux -shared -o libjnifunc.so jnifunc.cpp

コンパイルが完了すると現在のディレクトリの下にlibjnifuncが存在する.soファイル
ls lib*
libjnifunc.so

6 javaプログラムの実行
次の点に注意してください.
Javaプログラムが実行されると、まずダイナミックライブラリがロードされます.仮想マシンは、特定のパスの下で動的ライブラリ、すなわちjavaをロードする.library.pathプロパティで指定したパス.(詳細は、前回のブログJNIイニシャル--Hello JNIの例を参照)前回の記事では、java.library.pathプロパティで指定されたディレクトリにダイナミックライブラリを入れました.linuxシステムのダイナミックライブラリが変更されたため、良い方法ではありません.この例では、javaコマンドの実行時にlibjnifunc.soダイナミックライブラリがあるディレクトリを指定します.
次のコマンドを使用してjavaプログラムを実行します.
java -Djava.library.path='.' JniFuncMain

-Dオプションはjava実行時に設定された属性値を使用することを示します
. 現在のパスを表す、Systemを実行していることを示す.loadLibrary("jnifunc");に表示されます.
以下に、コンソールでのプログラムの出力を示します.
 java -Djava.library.path='.' JniFuncMain
java createJniObject()      
cpp  JniFuncMain   staticField          JniFuncMain.staticField = 300
cpp   niTest    
 C       ,        
 C   JniTest      callByNative. num = 200
cpp    JniTest   intField  200
 C   java   java       

-Dオプションを使用してパスを指定しない場合は、exportコマンドを使用してLD_を設定することもできます.LIBRARY_PATH環境変数、そしてjavaコマンドを実行します.
実は原理は同じで、ロードするダイナミックライブラリのパスを現在のディレクトリとして指定します.実行結果は次のとおりです.
zhangjg@MyUbuntu://home/zhangjg/JNITest/CreateJavaObjectInC$ export LD_LIBRARY_PATH=".":$LD_LIBRARY_PATH
zhangjg@MyUbuntu://home/zhangjg/JNITest/CreateJavaObjectInC$ java JniFuncMain
java createJniObject()      
cpp  JniFuncMain   staticField          JniFuncMain.staticField = 300
cpp   niTest    
 C       ,        
 C   JniTest      callByNative. num = 200
cpp    JniTest   intField  200
 C   java   java       

補足:
jniプログラムを書くとき、jniで定義されているタイプに惑わされたり、javaのタイプにどのように対応しているのか分からないことがあります.以下に、メモとして定義をいくつか示します.より詳細なタイプ定義はjni.hヘッダファイルに宣言します.jni.hファイルはJDKディレクトリの下のincludeディレクトリに存在します.
jni.h             
		
		typedef unsigned char	jboolean;
		typedef unsigned short	jchar;
		typedef short		jshort;
		typedef float		jfloat;
		typedef double		jdouble;
		typedef jint            jsize;

		struct _jfieldID;
		typedef struct _jfieldID *jfieldID;

		struct _jmethodID;
		typedef struct _jmethodID *jmethodID;
		
		class _jobject {};
		typedef _jobject *jobject;

		class _jclass : public _jobject {};
		typedef _jclass *jclass;
		
		class _jstring : public _jobject {};
		typedef _jstring *jstring;