在第一篇博客中 我们初步了解了jni编程的步骤,那接下来我认为极其重要的事情是搞清楚jni.h头文件里面的结构,包括数据类型和方法的定义等,这些是必须的,否则没有办法进行学习,就像写文章一样,要先学会写字是一样的道理。
首先来看一下jni.h头文件的组成:ps下面这张图是盗来的,我觉得这张图挺好的,莫怪莫怪,哈哈
下面我们就打开jni.h(位于jdk安装目录下的include文件夹下面)源码来分析一下这个结构
一、jni规范中定义的数据类型.
a> jni中的基本数据类型 对应的Java ,c/c++,jni 的数据类型参照如下:
Java类型 | 本地类型 | JNI定义的别名 | 字节数 |
int | long | jint/jsize | C/C++带符号的32位整型 |
long | _int64 | jlong | C/C++带符号的64位整型 |
byte | signed char | jbyte | C/C++带符号的8位整型 |
boolean | unsigned char | jboolean | C/C++8位整型 |
char | unsigned short | jchar | C/C++无符号的16位整型 |
short | shot | jshort | C/C++带符号的16位整型 |
float | float | jfloat | C/C++32位浮点型 |
double | double | jdouble | C/C++64位浮点型 |
Object | _jobject* | jobject |
在jni.h中源码定义如下:
/*
* JNI Types
*/
#ifndef JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H
typedef unsigned char jboolean;
typedef unsigned short jchar;
typedef short jshort;
typedef float jfloat;
typedef double jdouble;
typedef jint jsize;
b> 引用类型
java类型 | native类型 | 描述 |
---|---|---|
Object | jobject | 任何对象 |
Class | jclass | Class类对象 |
String | jstring | 字符串 |
Object[] | jobjectArray | 任何对象的数组 |
int[] | jintArray | int数组 |
char[] | jcharArray | 字符型对象 |
... | ... | ... |
二、jni中数组的定义
c++中所有的类都是继承至_jobject 类 是以类来定义的,c语言中是以结构体定义的如下:#else 下面的是对应的c语言对数组的定义,上面是c++对数组的定义
#ifdef __cplusplus
class _jobject {};
class _jclass : public _jobject {};
class _jthrowable : public _jobject {};
class _jstring : public _jobject {};
class _jarray : public _jobject {};
class _jbooleanArray : public _jarray {};
class _jbyteArray : public _jarray {};
class _jcharArray : public _jarray {};
class _jshortArray : public _jarray {};
class _jintArray : public _jarray {};
class _jlongArray : public _jarray {};
class _jfloatArray : public _jarray {};
class _jdoubleArray : public _jarray {};
class _jobjectArray : public _jarray {};
typedef _jobject *jobject;
typedef _jclass *jclass;
typedef _jthrowable *jthrowable;
typedef _jstring *jstring;
typedef _jarray *jarray;
typedef _jbooleanArray *jbooleanArray;
typedef _jbyteArray *jbyteArray;
typedef _jcharArray *jcharArray;
typedef _jshortArray *jshortArray;
typedef _jintArray *jintArray;
typedef _jlongArray *jlongArray;
typedef _jfloatArray *jfloatArray;
typedef _jdoubleArray *jdoubleArray;
typedef _jobjectArray *jobjectArray;
#else
struct _jobject;
typedef struct _jobject *jobject;
typedef jobject jclass;
typedef jobject jthrowable;
typedef jobject jstring;
typedef jobject jarray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray jshortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
typedef jarray jobjectArray;
#endif
三、方法签名时用到的公共体类型
typedef union jvalue {
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l;
} jvalue;
四、属性id的定义(获取Java类的属性)
struct _jfieldID;
typedef struct _jfieldID *jfieldID;
五、方法id的定义
struct _jmethodID;
typedef struct _jmethodID *jmethodID;
六、几种不同类型引用的定义
/* Return values from jobjectRefType */
typedef enum _jobjectType {
JNIInvalidRefType = 0,
JNILocalRefType = 1,
JNIGlobalRefType = 2,
JNIWeakGlobalRefType = 3
} jobjectRefType;
七、关于错误类型的定义
/*
* possible return values for JNI functions.
*/
#define JNI_OK 0 /* success */
#define JNI_ERR (-1) /* unknown error */
#define JNI_EDETACHED (-2) /* thread detached from the VM */
#define JNI_EVERSION (-3) /* JNI version error */
#define JNI_ENOMEM (-4) /* not enough memory */
#define JNI_EEXIST (-5) /* VM already created */
#define JNI_EINVAL (-6) /* invalid arguments */
八、释放数组是否关心拷贝的定义
/*
* used in ReleaseScalarArrayElements
*/
#define JNI_COMMIT 1
#define JNI_ABORT 2
九、JNI Native Method Interface.(java本地方法接口定义) 即 JNIEnv相关定义
/*
* JNI Native Method Interface.
*/
struct JNINativeInterface_;
struct JNIEnv_;
#ifdef __cplusplus
typedef JNIEnv_ JNIEnv;
#else
typedef const struct JNINativeInterface_ *JNIEnv;
#endif
十、java虚拟机接口
/*
* JNI Invocation Interface.
*/
struct JNIInvokeInterface_;
struct JavaVM_;
#ifdef __cplusplus
typedef JavaVM_ JavaVM;
#else
typedef const struct JNIInvokeInterface_ *JavaVM;
#endif
以上就是jni.h头文件中 几大块的定义。
那么JNIEnv,jobject,jclass,从源码中看到了这几块的定义,在第一篇的博客中也简单分析了这三个含义,但是我觉得还不够。
JNIEnv 分析:
C++中:JNIEnv就是struct _JNIEnv。 JNIEnv* env等价于struct _JNIEnv*env,在调用JNI函数的时候,只需要env-> FindClass(JNIEnv*, const char*),就会间接调用JNINativeInterface结构体里定义的函数指针,而无需首先对env解引用。
C中:JNIEnv就是const struct JNINativeInterface*。JNIEnv* env实际等价于const struct JNINativeInterface** env,因此要得到JNINativeInterface结构体内的函数指针就必须先对env解引用得到(*env),即const struct JNINativeInterface*,这个指针才是真正指向JNINativeInterface结构体的指针,然后再通过它调用具体的JNI函数。因此需要这样调用:(*env)-> FindClass(JNIEnv*, const char*)。
注意: JNIEnv只在当前线程中有效。本地方法不能将JNIEnv从一个线程传递到另一个线程中。相同的 Java 线程中对本地方法多次调用时,传递给该本地方法的JNIEnv是相同的。但是,一个本地方法可被不同的 Java 线程所调用,因此可以接受不同的 JNIEnv。
看一下JNINativeInterface_里面是什么:(截取部分源码)
struct JNINativeInterface_ {
void *reserved0;
void *reserved1;
void *reserved2;
void *reserved3;
jint (JNICALL *GetVersion)(JNIEnv *env);
jclass (JNICALL *GetObjectClass)
(JNIEnv *env, jobject obj);
jboolean (JNICALL *IsInstanceOf)
(JNIEnv *env, jobject obj, jclass clazz);
jmethodID (JNICALL *GetMethodID)
(JNIEnv *env, jclass clazz, const char *name, const char *sig);
jobject (JNICALL *CallObjectMethod)
(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jobject (JNICALL *CallObjectMethodV)
(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jboolean (JNICALL *CallBooleanMethod)
(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jboolean (JNICALL *CallBooleanMethodV)
(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jbyte (JNICALL *CallByteMethod)
(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jbyte (JNICALL *CallByteMethodV)
(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jchar (JNICALL *CallCharMethod)
(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jchar (JNICALL *CallCharMethodV)
(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
.......
void (JNICALL *CallVoidMethod)
(JNIEnv *env, jobject obj, jmethodID methodID, ...);
void (JNICALL *CallVoidMethodV)
(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
void (JNICALL *CallVoidMethodA)
(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args);
......
jobjectArray (JNICALL *NewObjectArray)
(JNIEnv *env, jsize len, jclass clazz, jobject init);
jobject (JNICALL *GetObjectArrayElement)
(JNIEnv *env, jobjectArray array, jsize index);
void (JNICALL *SetObjectArrayElement)
(JNIEnv *env, jobjectArray array, jsize index, jobject val);
jbooleanArray (JNICALL *NewBooleanArray)
(JNIEnv *env, jsize len);
jbyteArray (JNICALL *NewByteArray)
(JNIEnv *env, jsize len);
jcharArray (JNICALL *NewCharArray)
(JNIEnv *env, jsize len);
jshortArray (JNICALL *NewShortArray)
jboolean * (JNICALL *GetBooleanArrayElements)
(JNIEnv *env, jbooleanArray array, jboolean *isCopy);
jbyte * (JNICALL *GetByteArrayElements)
(JNIEnv *env, jbyteArray array, jboolean *isCopy);
jchar * (JNICALL *GetCharArrayElements)
(JNIEnv *env, jcharArray array, jboolean *isCopy);
...........
void (JNICALL *ReleaseByteArrayElements)
(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode);
..........
};
看一下JNIEnv_里面是什么:(截取部分源码)
struct JNIEnv_ {
const struct JNINativeInterface_ *functions;
#ifdef __cplusplus
jint GetVersion() {
return functions->GetVersion(this);
}
jclass DefineClass(const char *name, jobject loader, const jbyte *buf,
jsize len) {
return functions->DefineClass(this, name, loader, buf, len);
}
jclass FindClass(const char *name) {
return functions->FindClass(this, name);
}
........................
jobject NewObject(jclass clazz, jmethodID methodID, ...) {
va_list args;
jobject result;
va_start(args, methodID);
result = functions->NewObjectV(this,clazz,methodID,args);
va_end(args);
return result;
}
jclass GetObjectClass(jobject obj) {
return functions->GetObjectClass(this,obj);
}
jboolean IsInstanceOf(jobject obj, jclass clazz) {
return functions->IsInstanceOf(this,obj,clazz);
}
jmethodID GetMethodID(jclass clazz, const char *name,
const char *sig) {
return functions->GetMethodID(this,clazz,name,sig);
}
jobject CallObjectMethod(jobject obj, jmethodID methodID, ...) {
va_list args;
jobject result;
va_start(args,methodID);
result = functions->CallObjectMethodV(this,obj,methodID,args);
va_end(args);
return result;
}
.....................................................
jobject GetObjectField(jobject obj, jfieldID fieldID) {
return functions->GetObjectField(this,obj,fieldID);
}
jboolean GetBooleanField(jobject obj, jfieldID fieldID) {
return functions->GetBooleanField(this,obj,fieldID);
}
jbyte GetByteField(jobject obj, jfieldID fieldID) {
return functions->GetByteField(this,obj,fieldID);
}
void SetObjectField(jobject obj, jfieldID fieldID, jobject val) {
functions->SetObjectField(this,obj,fieldID,val);
}
......................................
jchar CallStaticCharMethod(jclass clazz,
jmethodID methodID, ...) {
va_list args;
jchar result;
va_start(args,methodID);
result = functions->CallStaticCharMethodV(this,clazz,methodID,args);
va_end(args);
return result;
}
jstring NewString(const jchar *unicode, jsize len) {
return functions->NewString(this,unicode,len);
}
jsize GetStringLength(jstring str) {
return functions->GetStringLength(this,str);
}
................................
jobjectArray NewObjectArray(jsize len, jclass clazz,
jobject init) {
return functions->NewObjectArray(this,len,clazz,init);
}
jobject GetObjectArrayElement(jobjectArray array, jsize index) {
return functions->GetObjectArrayElement(this,array,index);
}
void SetObjectArrayElement(jobjectArray array, jsize index,
jobject val) {
functions->SetObjectArrayElement(this,array,index,val);
}
...............................................
void ReleaseByteArrayElements(jbyteArray array,
jbyte *elems,
jint mode) {
functions->ReleaseByteArrayElements(this,array,elems,mode);
}
void ReleaseCharArrayElements(jcharArray array,
jchar *elems,
jint mode) {
functions->ReleaseCharArrayElements(this,array,elems,mode);
}
void ReleaseShortArrayElements(jshortArray array,
jshort *elems,
jint mode) {
functions->ReleaseShortArrayElements(this,array,elems,mode);
}
void ReleaseIntArrayElements(jintArray array,
jint *elems,
jint mode) {
functions->ReleaseIntArrayElements(this,array,elems,mode);
}
void ReleaseLongArrayElements(jlongArray array,
jlong *elems,
jint mode) {
functions->ReleaseLongArrayElements(this,array,elems,mode);
}
void ReleaseFloatArrayElements(jfloatArray array,
jfloat *elems,
jint mode) {
functions->ReleaseFloatArrayElements(this,array,elems,mode);
}
void ReleaseDoubleArrayElements(jdoubleArray array,
jdouble *elems,
jint mode) {
functions->ReleaseDoubleArrayElements(this,array,elems,mode);
}
...............
jobjectRefType GetObjectRefType(jobject obj) {
return functions->GetObjectRefType(this, obj);
}
#endif /* __cplusplus */
};
看了源码就终于明白了,为什么我们可以用JNIEnv来调用很多api方法了,以及c和c++调用方法的时候为什么写法不同。
jobject,与jclass 分析:
jobject第一篇博客提到了,jobject 就代表了 java中包含native方法的类的一个实例,准确来说就是调用本地方法的那个实例。
jclass 代表的是一个类对象,而不是一个类的实例。如下图:盗来的,哈哈。。。。
第一个方法是对应Java类中的非静态 native方法,对应的第二个参数是jboject
第二个方法是对应的Java类中的静态(static修饰)native方法,第二个参数对应的是jclass.
why???????
因为,静态方法与静态变量一样,属于类本身,而不属于那个类的一个对象。调用一个被定义为static的方法,可以通过在它前面加上这个类的名称,也可以像调用非静态方法一样通过类对象调用。
实例方法必须通过类的实例来使用。实例方法可以使用类的非静态成员,也可以使用类的静态成员。
类的静态方法,静态变量是在类装载的时候装载的。但是要特别注意,类的静态变量是该类的对象所共有的,即是所有对象共享变量。所以建议尽量少用静态变量。尽量在静态方法中使用内部变量。
在C/C++中调用 Java类中的属性和方法
继续使用上一篇博客的例子
java端代码:和上一篇相比多定义了一个 num 属性和 getMax方法
package com.koimy;
public class Main {
int num = 123;
static {
System.loadLibrary("NativeCode");
}
public native void sayHello();
public int getMax(int num1, int num2) {
return num1 > num2 ? num1 : num2;
}
public static void main(String[] args) {
Main main = new Main();
main.sayHello();
System.out.println("Java: 这是Java输出得num = " + main.num);
}
}
C++端代码:获取Java端 main这个对象的的num 属性的值输出到控制台,并修改他的值,同时调用getMax方法
#include<iostream>
#include"com_koimy_Main.h"
using namespace std;
JNIEXPORT void JNICALL Java_com_koimy_Main_sayHello (JNIEnv *env, jobject obj)
{
// cout<<"This message is from JNI by C++!"<<endl;
jclass class_nativecode = env->GetObjectClass(obj);
//===========================c/c++调用Java类得属性==========================================
jfieldID id_num = env->GetFieldID(class_nativecode,"num","I");
jint num = env->GetIntField(obj,id_num);
cout<<"C++: num = "<<num<<endl;
env->SetIntField(obj,id_num,666L);
//===========================c/c++调用Java类的方法==========================================
jmethodID id_getmax = env->GetMethodID(class_nativecode,"getMax","(II)I");
jint max = env->CallIntMethod(obj,id_getmax,100L,36L);
cout<<"C++: max = "<<max<<endl;
}
最后控制台输出如下:
调用属性和方法的步骤:
1.获得jclass
2.获得 jfiledID/ jmethodID
3.通过env调用 get<TYPE>Field() 或者调用Call<Type>Method();
OK就这么多吧!
PS:欢迎加入我的qq群一起学习:239074811