1. 本地代码拜访Java代码
- 在被调用的C/C++函数中也能够反过来拜访Java程序中的办法
- javah 工具生成的C/C++函数申明中,能够看到有两个参数:
JNIEXPORT void JNICALL Java_com_omg_NativeLib_sayHello (JNIEnv *env, jobject jobj){ cout << "hello world" << endl;}
2. JNIEnv 类型
- JNIENV类型实际上代表了Java环境,通过这个JNIEnv*指针,就能够对Java端的代码进行操作。例如,创立Java类的对象、调用Java对象的办法、获取Java对象的属性等。JNIEnv的指针会被JNI传入到本地办法的实现函数中来对Java端的代码进行操作。
- JNIEnv类中有很多函数能够应用
NewObject / NewString / New<TYPE>ArrayGet<TYPE>Field / Set<TYPE>FieldGetStatic<TYPE>Field / SetStatic<TYPE>FieldCall<TYPE>Method / CallStatic<TYPE>Method
等许许多多的函数
- jobject 什么的一个类型呢?
不是动态形式时,传入的是以后关联的类对象
静态方法时,传入以后类的Class对象
3. Java的类型在C/C++中的映射关系
Java类型 | 本地类型 | JNI定义的别名 |
---|---|---|
int | long | jint / jsize |
long | __int64 | jlong |
byte | signed char | jbyte |
boolean | unsigned char | jboolean |
char | unsigned short | jchar |
short | short | jshort |
float | float | jfloat |
double | double | jdouble |
Object | _jobject* | jobject |
4. jclass如何获取
- 为了可能在C/C++中应用Java类。JNI.h头文件中,专门定义了jclass类型来示意Java中的Class类
- JNIEnv类中有以下几个函数能够获取jclass:
jclass FindClass(const char* clsName);jclass GetObjectClass(jobject ojb);jclass GetSuperClass(jclass obj);
在Java JDK中也有这几个办法
- FindClass 将会在classpath环境变量下寻找类。传入残缺类名,留神包与包之间是应用 “/ “ ,而不是应用 “.“ 来分隔
jclass cls_str = env->FindClass("java/lang/String");
5. 拜访Java类中的属性与办法
- 在C/C++本地代码中拜访Java端的代码,一个常见的场景就是获取类的属性和调用类的办法。为了在C/C++中示意属性和办法,JNI在jni.h头文件中定义了jfieldID和jmethdID类型来别离代表Java端的属性和办法
- 在拜访,或者设置Java属性的时候,首先就要先在本地代码中取得代表Java属性的jfieldID;而后能力在本地代码中进行Java的属性操作。同样的,在须要调用Java的办法时,也是须要先获取到代表该办法的jmethodID能力进行Java办法调用
应用JNIEnv的
GetFieldID / GetMethodIDGetStaticFieldID / GetStaticMethodID
来获得相应的jfieldID和jmethodID
来看下办法定义
jfieldID GetFieldID(jclass clazz, const char *name, const char *sig);jmethodID GetMethodID(jclass clazz, const char *name, const char *sig);jfieldID GetStaticFieldID(jclass clazz, const char *name, const char *sig);jmethodID GetStaticMethodID(jclass clazz, const char *name, const char *sig);
- GetMethodID也可能取得构造函数的jmethodID。创立一个Java对象时能够调用指定的构造方法,这个将在前面向大家介绍
如:env -> GetMethodID(data_Clazz, "<init>", "()V");
- 相似Java的发射(Reflect),须要指定类跟属性/办法名来获取相应的jfieldID或者jmethodID。而sign又是什么呢?
6. Sign是什么?
例如:TestNative类中有两个重载的办法
package com.omg;public class TestNative { public void function( int i ){ System.out.println("Integer: " + i); } public void function( double d ){ System.out.println("double: " + d); }}
而后再C/C++代码中调用其中的一个function办法的话;
//首先获得要调用的办法所在的类jclass clazz_TestNative = env->FindClass("com/omg/TestNative");//获取jmethodID之后能力进行调用jmethodID id_func = env->GetMethodID(clazz_TestNative, "function", ??);
然而到底获得的是 void function(int i) 办法还是 void function(double d) 办法的jmethodID呢?
这就是sign的作用了,他用于指定要获得的属性/办法的类型。
这里的sign如果指定为"(I)V",则取得的是 void function(int i) 办法的jmethodID;如果指定为"(D)V",则取得的是 void function(double d) 办法的jmethodID
7. sign签名
用来示意要获得的属性/办法的类型
类型 | 签名 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | L |
float | F |
double | D |
void | V |
object | L + 用/分隔包的残缺类名 + ; |
Array | [ + 签名 例如,[I、[Ljava/lang/String; |
Method | (参数1类型签名 参数2类型签名...)返回值类型签名 |
8. 应用签名取得属性/办法ID的例子(简单)
import java.util.Date;public class HelloNative(){ public int property; public int function(int foo, Date date, int[] arr){ System.out.println("function"); } public native void test();}
test本地办法的实现
JNIEXPORT void Java_Hello_test(JNIEnv* env, jboject ojb) { //因为test不是动态函数,所以传进来的就是调用这个函数的对象 jclass hello_clazz = env->GetObjectClass(obj); jfieldID filedID_prop = env->GetFieldID(hello_clazz, "property", "I"); jmethodID methodID_func = env->GetMethodID("hello_clazz", "function", "(ILjava/util/Date;[I)I"); env->CallIntMethod(obj, methodID_func, 0L, NULL, NULL); //invoke}
获得的property是int类型的,所以在签名中传入"I"
获得function的ID时
所以在最初失去 (ILjava/util/Date;[I)I
9. 应用javap命令来产生签名
- JDK提供了一个工具来查看一个类的申请,其中就能够设置输入每一个办法/属性的签名
- java -s -p [full Calss Name]
-s 示意输入签名信息
-p 通-private,输入包含private拜访权限的成员信息