说明:
本实例代码来自于《Android 框架揭秘》
程序运行环境 -- Ubuntu 12.04 64bit, JDK1.6, g++ 4.6.3 。
/** * 在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中进行方法调用"); } }
javac JniFuncMain.java
ls JniFuncMain.class JniFuncMain.class
javah JniFuncMain
ls 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
该实例演示了以下知识点:
在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\n", staticIntField); //查找生成对象的类 targetClass = env->FindClass("JniTest"); //查找该类的构造方法 mid = env->GetMethodID(targetClass, "<init>", "(I)V"); //生成JniTest类的对象, 返回该对象的引用 printf("cpp中生成niTest类的对象\n"); 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\n"); env->SetIntField(newObject, fid, result); //返回对象的引用 //虽然在java中刚得到这个对象, 但在cpp中已经对这个对象进行了操作, 例如调用了方法, 设置了成员变量的值 return newObject; }
有三个知识点需要注意:
1) linux下的动态库必须以lib开头, 格式为 lib库名称.so
2) 编译cpp文件使用linux中的g++命令
3) JniFuncMain.h中包含了jdk中的两个头文件, 需要在g++命令中指定这两个头文件的路径
编译命令如下:
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
ls lib* libjnifunc.so
注意以下问题:
在java程序运行时, 首先会加载动态库。虚拟机在特定的路径下加载动态库,即java.library.path属性指定的路径。(详细信息见上一篇博客JNI初步 -- Hello JNI示例)在上篇文章中, 我们把动态库放入了java.library.path属性指定的一个目录中。这不是一个好的方法,因为修改了linux系统的动态库。在本示例中,将在java命令执行时指定libjnifunc.so动态库所在的目录。
使用以下命令执行java程序:
java -Djava.library.path='.' JniFuncMain
. 代表当前路径, 表示在执行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中进行方法调用
其实原理是一样的, 都是指定要加载的动态库的路径为当前目录。执行结果如下:
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.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;