乐趣区

关于java:JNI本地代码调用Java代码

 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>Array
Get<TYPE>Field / Set<TYPE>Field
GetStatic<TYPE>Field / SetStatic<TYPE>Field
Call<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 / GetMethodID
GetStaticFieldID / 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 拜访权限的成员信息

退出移动版