JNI(调用JNI函数)
示例程序运行顺序
- 调用JNI本地函数
- 访问静态成员获取值
- 创建JniTest对象
- 调用JniTest对象的方法
- 传递返回值
- 访问成员变量设定其值
JniFuncMain.java
public class JniFuncMain
{
private static int staticIntField = 300;
//加载本地库jinfunc.dll
static {System.loadLibrary("jnifunc");}
//本地方法声明
public static native JniTest createJniObject();
public static void main(String[] args)
{
//从本地代码生成JniTest对象
System.out.println("[Java]createJniObject()调用本地方法");
//使用static关键字,不需要创建对象,直接通过JniFuncMain类调用即可
JniTest jniObj = createJniObject();
//调用JniTest对象的方法
jniObj.callTest();
}
}
JniTest.java
public class JniTest
{
private int intField;
//构造方法
public JniTest(int num)
{
intField = num;
System.out.println("[Java] 调用JniTest对象的构造方法:intField = " + intField);
}
//此方法由JNI本地函数调用
public int callByNative(int num)
{
System.out.println("[Java] JniTest对象的callByNative(" + num + ")调用");
return num;
}
public void callTest()
{
System.out.println("[Java] JniTest对象的callTest()方法调用:intField="+intField);
}
}
JniFuncMain.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#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
jnifunc.cpp
#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类的staticIntField变量值
fid = env->GetStaticFieldID(clazz,"staticIntField","I");
//读取jclass与fieldid指定的成员变量值
staticIntField = env->GetStaticIntField(clazz,fid);
printf("[CPP]获取JniFuncMain类的staticIntField变量值\n");
printf(" JniFuncMain.staticIntField = %d\n", staticIntField);
//查找生成对象的类
targetClass = env->FindClass("JniTest");
//查找构造方法
mid = env->GetMethodID(targetClass,"<init>","(I)V");
//生成JniTest对象(返回对象的引用)
printf("[CPP]JniTest对象生成\n");
newObject = env->NewObject(targetClass,mid,100);
//调用对象的方法
mid = env->GetMethodID(targetClass,"callByNative","(I)I");
result = env->CallIntMethod(newObject,mid,200);
//设置JniObject对象的intField值
fid = env->GetFieldID(targetClass,"intField","I");
printf("[CPP]设置JniObject对象的intField值为200\n");
env->SetIntField(newObject,fid,result);
//返回对象的引用
return newObject;
}
编译命令
gcc -finput-charset=UTF-8 -fexec-charset=GBK -shared -o jnifunc.dll jnifunc.cpp
运行命令
java JniFuncMain
输出结果
[Java]createJniObject()调用本地方法
[CPP]获取JniFuncMain类的staticIntField变量值
JniFuncMain.staticIntField = 300
[CPP]JniTest对象生成
[Java] 调用JniTest对象的构造方法:intField = 100
[Java] JniTest对象的callByNative(200)调用
[CPP]设置JniObject对象的intField值为200
[Java] JniTest对象的callTest()方法调用:intField=200
访问Java类/对象的成员变量
步骤:
- 查找含待访问的成员变量的Java类的jclass值
- 查找此类成员变量的jfieldID值
- 使用jclass与jfieldID值,获取或设置成员变量值
若想要在本地代码中访问Java的成员变量,必须获取相应成员变量的ID值。
成员变量的ID保存在jfieldID类型的变量中。由于待读取数值的staticIntField成员变量是JniFuncMain类的静态成员变量,在获取staticIntField的ID时,应调用名称为GetStaticFieldID()的JNI函数
若想获取普通对象中的非静态成员变量的ID,应调用名称为GetFieldID()的JNI函数
获取jclass值
FindClass()
形式:
jclass FindClass(JNIEnv *env,const char *name)
说明:
查找name指定的Java类参数
env JNI接口指针
name 待查找的类名返回值
返回类的jclass值
GetObjectClass()
形式:
jclass GetObjectClass(JNIEnv *env, jobject obj)
功能:
通过对象获取这个类。该函数比较简单,唯一注意的是对象不能为NULL,否则获取的class肯定返回也为NULL。
参数:
- env JNI 接口指针。
- obj Java 类对象实例。
获取jfieldID值
GetStaticFieldID()
形式:
jfield GetStaticFieldID(JNIEnv *env,jclass clazz,const char *name,const *signature)
说明:
返回指定类的指定的静态成员变量的jfieldID的值参数:
- env JNI接口指针
- clazz 包含成员变量的类的jclass
- name 成员变量名
- signature 成员变量签名
GetFieldID()
形式:
jfield GetFieldID(JNIEnv *env,jclass clazz,const char *name,const *signature)
说明:
返回对象中指定的成员变量的jfieldID的值参数:
- env JNI接口指针
- clazz 包含成员变量的类的jclass
- name 成员变量名
- signature 成员变量签名
获取签名
在JNI中获取成员变量或成员方法签名
形式:javap [选项] ‘类名’
选项:
- -s 输出Java签名
- -p 输出所有类及成员
javap -s -p JniFuncMain
输出结果
Compiled from "JniFuncMain.java"
public class JniFuncMain {
private static int staticIntField;
descriptor: I //签名
public JniFuncMain();
descriptor: ()V
public static native JniTest createJniObject();
descriptor: ()LJniTest;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
static {};
descriptor: ()V
}
获取成员变量值
在获取成员变量所在的类的ID后,根据个成员变量的类型与存储区块,调用相应的JNI函数读取成员变量值即可。在JNI中有两种函数用来获取成员变量的值,分别为Get<type>Field
函数与GetStatic<type>Field
函数(<type>
指Int、Char、Double等基本数据类型,具体参考JNI文档)
GetStatic<type>Field()
形式:
<jnitype> GetStatic<type>Field(JNIEnv *env,jclass clazz,jfieldID fieldID)
说明:
返回clazz类中ID为fieldID的静态变量的值参数:
- env JNI接口指针
- clazz 包含成员变量的类
- fieldID 成员变量的ID
参考:
<type>
指Object、Boolean、Byte、Char、Short、Int、Long、Float、Double九种基本类型。
返回类型<jnitype>
指jobject、jboolean、jbyte、jchar、jshort、jint、jlong、jfloat、jdouble九种基本类型
这些类型也被应用到其他JNI函数的<type>
中返回值
返回静态成员变量的值
Get<type>Field()
形式:
<jnitype> Get<type>Field(JNIEnv *env,jobject obj,jfieldID fieldID)
说明:
返回obj对象中ID为fieldID的成员变量的值参数:
- env JNI接口指针
- obj 包含成员变量的对象
- fieldID 成员变量的ID
返回值
返回成员变量的值
生成Java对象
步骤:
- 查找指定的类,并将查找到的类赋给jclass类型的变量
- 查找Java类构造方法的ID值(类型为jmethodID)
- 生成Java类对象
首先调用JNi函数FindClass(),查找生成对象的类。
获取jmethodID值
在JNI函数中有一个GetMethodID()
函数用来获取指定类的指定方法ID.此函数除了可以用来获取指定类的构造方法的ID外,还可以获取类的其他方法的ID。若指定的方法是静态方法,则可以调用JNI函数中的GetStaticMethodID()函数,获取指定静态方法的ID
在生成指定类的对象之前,需要先调用GetMethodID()函数获取该类构造方法的ID。在调用GetMethodID()函数时,除了提供生成对象的类,还要提供类的构造方法名称(类的构造方法名称为“<init>
”,其他非构造方法,直接提供方法名即可),以及构造方法的签名。
GetMethodID()
形式:
jmethodID GetMethodID(JNIEnv *env,jclass clazz,const char *name,const char *signature)
说明:
获取clazz类对象的指定方法的ID。注意,方法名(name)与签名应当保持一致。若获取类构造方法的ID,方法名为”<init>
“参数:
- env JNI接口指针
- clazz Java类
- name 方法名
- signature 方法签名
返回值
若方法ID错误,则返回NULL
生成Java类对象
以获得的JniTest类的jclass与构造方法的ID为参数,调用JNI函数NewObject(),生成JniTest类的对象。JniTest类的构造方法JniTest(int num)带有一个int类型的参数,在调用NewObject()时,同时传入100这个int数据。在生成JniTest类的对象后,将对象的引用保存在jobject变量中。
NewObject()
形式:
jobject NewObject(JNIEnv *env,jclass clazz,jmethodID methodID,...)
说明:
生成指定类的对象。methodID指类的构造方法的ID参数:
- env JNI接口指针
- clazz Java类
- methodID 类的构造方法的ID
- … 传递给类的构造方法的参数
返回值
放回类对象的引用。若发生错误,返回NULL
调用Java方法
步骤:
- 获取含待调方法的Java类的jclass
- 获取待调方法的ID
- 调用Java方法保存返回值
CallStatic<type>Method()
形式:
<jnitype> CallStatic<type>Method(JNIEnv *env,jclass clazz,jmethodID methodID,...)
说明:
调用methodID指定的类的静态方法参数:
- env JNI接口指针
- clazz 含待调方法的类
- methodID 待调方法的ID
- … 传递给待调方法的参数
返回值:
被调方法的返回值参考:
<type>
指Object、Boolean、Byte、Char、Short、Int、Long、Float、Double、void十种基本类型。
返回类型<jnitype>
指jobject、jboolean、jbyte、jchar、jshort、jint、jlong、jfloat、jdouble、void十基本类型
待调方法的返回值不同<type>
也不同。若待调方法的返回值类型为int,则调用函数为CallStaticIntMethod()
Call<type>Method()
形式:
<jnitype> Call<type>Mrthod(JNIEnv *env,jobject obj,jmethodID method,...)
说明:
调用methodID指定的Java对象的方法参数:
- env JNI接口指针
- obj 含待调方法的Java对象的方法
- methodID 待调方法的ID
- … 传递给待调方法的参数
返回值:
被调方法的返回值
设置成员变量的值
步骤:
- 获取成员变量所在类的jclass值
- 获取对象的变量值
- 设置变量值
SetStatic<type>Field()
形式:
void SetStatic<type>Field(JNIEnv *env,jclass clazz,jfieldID field,<type> value)
说明:
设置fieldID指定的Java类静态成员变量的值
参数:
- env JNI接口指针
- clazz 含待设置成员变量的类的引用
- fieldID 待设成员变量的ID
- value 指定设置值
Set<type>Field()
形式
void Set<type>Field(JNIEnv *env,jobject obj,jfieldID field,<type> value)
说明:
设置fieldID指定的Java对象的成员变量的值
参数:
- env JNI接口指针
- obj 含待设置成员变量的Java对象的引用
- fieldID 待设成员变量的ID
- value 指定设置值
局部引用与全局引用
在实现JNI本地函数时,由GetObjectClass()、FindClass()等JNI函数返回的jclass、jobject等引用都是局部引用。
局部引用是JNI默认的,它仅在JNI本地函数内才有效,即当JNI本地函数返回后,其内部的引用就会失效。
JNI提供了一个名称为NewGlobalRef()的JNI函数,用来为指定的类或对象生成全局引用(Global Reference),以便在JNI本地函数中在全局范围内使用该引用
NewGlobalRef()
形式:
jobject NewGlobalRef(JNIEnv *env,jobject obj)说明:
为obj指定的类或对象,生成全局引用参数:
- env JNI接口指针
- obj 待生成全局引用的引用值
返回生成的全局引用,若发生错误,则返回NULL
当全局引用使用完毕后,应当调用名称为DeleteGlobalRef()的JNI函数,显性地将全局引用销毁。
例子
#include "RefTestMain.h"
static jclass globalTargetClass = 0;
JNIEXPORT jint JNICALL Java_RefTestMain_getMember(JNIEnv *env,jclass clazz)
{
jfieldID fid;
jint intField;
jclass targetClass;
if(globalTargetClass == 0)
{
targetClass = env->FindClass("RefTest");
globalTargetClass =(jclass) env->NewGlobalRef(targetClass);
}
fid = env->GetStaticFieldID(globalTargetClass,"intField","I");
intField = env->GetStaticIntField(globalTargetClass,fid);
return intField;
}