在第一篇博客中 我们初步了解了jni编程的步骤,那接下来我认为极其重要的事情是搞清楚jni.h头文件里面的结构,包括数据类型和方法的定义等,这些是必须的,否则没有办法进行学习,就像写文章一样,要先学会写字是一样的道理。

首先来看一下jni.h头文件的组成:ps下面这张图是盗来的,我觉得这张图挺好的,莫怪莫怪,哈哈

android头文件include 头文件java_#define

下面我们就打开jni.h(位于jdk安装目录下的include文件夹下面)源码来分析一下这个结构


一、jni规范中定义的数据类型.

   a> jni中的基本数据类型 对应的Java ,c/c++,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。

android头文件include 头文件java_整型_02

看一下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 代表的是一个类对象,而不是一个类的实例。如下图:盗来的,哈哈。。。。

android头文件include 头文件java_#define_03

第一个方法是对应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;

}

最后控制台输出如下:

android头文件include 头文件java_android头文件include_04

 

调用属性和方法的步骤:

1.获得jclass

2.获得 jfiledID/ jmethodID

3.通过env调用 get<TYPE>Field() 或者调用Call<Type>Method();

OK就这么多吧!