JNIイニシャル--CPPでJavaオブジェクトを作成してアクセス
説明:
このインスタンスコードは、「Androidフレームワークの秘密」から来ています.
プログラム実行環境--Ubuntu 12.04 64 bit,JDK 1.6, g++ 4.6.3 .
1 javaファイルの作成
2 javaファイルのコンパイル
3ヘッダファイルの生成
ヘッダファイルJniFuncMain.hの内容は以下の通りである.
4 CPP実装ファイルの作成
この例では、次の点について説明します.
CPPでJavaの静的メソッドをどのように呼び出すか、
Javaオブジェクトを作成して戻す方法
Javaオブジェクトのメンバーメソッドを呼び出すには、
Javaオブジェクトのメンバー変数の変更方法
jnifunc.cppのファイル内容は以下の通りです.
5 CPPファイルを動的ライブラリとしてコンパイル
3つの知識点に注意してください.
1)linuxの動的ライブラリはlibで始まる、libライブラリ名である必要がある.so
2)cppファイルをコンパイルlinuxのg++コマンドを使用する
3) JniFuncMain.hにはjdkの2つのヘッダファイルが含まれており、g++コマンドで2つのヘッダファイルのパスを指定する必要があります.
コンパイルコマンドは次のとおりです.
コンパイルが完了すると現在のディレクトリの下にlibjnifuncが存在する.soファイル
6 javaプログラムの実行
次の点に注意してください.
Javaプログラムが実行されると、まずダイナミックライブラリがロードされます.仮想マシンは、特定のパスの下で動的ライブラリ、すなわちjavaをロードする.library.pathプロパティで指定したパス.(詳細は、前回のブログJNIイニシャル--Hello JNIの例を参照)前回の記事では、java.library.pathプロパティで指定されたディレクトリにダイナミックライブラリを入れました.linuxシステムのダイナミックライブラリが変更されたため、良い方法ではありません.この例では、javaコマンドの実行時にlibjnifunc.soダイナミックライブラリがあるディレクトリを指定します.
次のコマンドを使用してjavaプログラムを実行します.
-Dオプションはjava実行時に設定された属性値を使用することを示します
. 現在のパスを表す、Systemを実行していることを示す.loadLibrary("jnifunc");に表示されます.
以下に、コンソールでのプログラムの出力を示します.
-Dオプションを使用してパスを指定しない場合は、exportコマンドを使用してLD_を設定することもできます.LIBRARY_PATH環境変数、そしてjavaコマンドを実行します.
実は原理は同じで、ロードするダイナミックライブラリのパスを現在のディレクトリとして指定します.実行結果は次のとおりです.
補足:
jniプログラムを書くとき、jniで定義されているタイプに惑わされたり、javaのタイプにどのように対応しているのか分からないことがあります.以下に、メモとして定義をいくつか示します.より詳細なタイプ定義はjni.hヘッダファイルに宣言します.jni.hファイルはJDKディレクトリの下のincludeディレクトリに存在します.
このインスタンスコードは、「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;