JNI学习笔记:C++代码访问Java类中的成员和方法



0 前言

在大型的JNI开发项目中,JNI接口的使用可能是方方面面的,函数的参数并不会局限于常见的基本数据类型,更有可能是对象、方法或者集合等复合数据类型。

开发环境

  • Java 8
  • Visual Studio 2013 Ultimate

1 程序一:通过字段访问Java类的成员

1.1 代码

  • Java代码
public class filedTest{
    static{
        System.loadLibrary("JNITest");
    }
    public native void modifiedString();
    private String str = "Hello,C++,I'm a Java String!";
    public static void main(String args[]){
        filedTest ft = new filedTest();
        ft.modifiedString();
        System.out.println("In Java:");
        System.out.println("After calling the native function:");
        System.out.println(ft.str);
    }
}
  • native代码
#include<jni.h>
#include<iostream>
#include<algorithm>

using namespace std;

JNIEXPORT void JNICALL Java_filedTest_modifiedString
(JNIEnv*env, jobject obj){
    // 获取jclass
    jclass jcla = env->GetObjectClass(obj);
    // 获取filedID
    jfieldID jfieid = env->GetFieldID(jcla,"str","Ljava/lang/String;");
    // 获取字段的值, 注意,String是Object的子类,需要进行强制转换
    jstring jstr = (jstring)env->GetObjectField(obj, jfieid);
    // 从jstring中取字符串
    const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
    cout << "In C:" << endl << str << endl;
    const char*new_str = "Hi,Java,I'm C string.";
    // 设置新的String
    jstring new_jstr = env->NewStringUTF(new_str);
    // 通过字段ID,访问Java的String成员
    env->SetObjectField(obj, jfieid, (jobject)new_jstr);
}
  • 运行结果
    运行结果

显而易见,str成员被修改。

1.2 函数分析

jclass GetObjectClass(jobject obj);

  • jobject obj 得到传入JNI的Java对象的句柄

jfieldID GetFieldID(jclass clazz, const char *name,const char *sig);

  • jclass clazzJava对象的句柄
  • const char *name 所要访问对象的成员名
  • const char *sig 所要访问对象的类型签名
  • jfieldID 返回字段的ID

jobject GetObjectField(jobject obj, jfieldID fieldID);

  • jobject obj 所要访问Java对象的句柄
  • jfieldID fieldID 所要访问对象成员字段的ID
  • jobject 返回字段ID对应的值,此处为一个对象类型

const char* GetStringUTFChars(jstring str, jboolean *isCopy) ;

  • jstring str 传入UTF-8的Java 字符串String对象
    -jboolean *isCopy 是否为字符串拷贝,*isCopy = JNI_FALSE,表示可以在原始字符串上进行操作,JNI_TRUE表示是字符串的拷贝,不会影响到字符串原值
  • const char* 返回值是一个C类型的字符串

jstring NewStringUTF(const char *utf);

  • const char *utf 传入要复制的C类型的字符串
  • jstring 根据传入的C类型的字符串,返回一个Java String对象

void SetObjectField(jobject obj, jfieldID fieldID, jobject val) ;

  • jobject obj 将要访问的Java对象
  • jfieID fieId将要访问的对象字段ID
  • jobject val 对应要修改的值

1.3 总结和补充

通过JNI接口访问Java对象的成员的流程,大体如下:
程序流程

访问静态成员和非静态成员会有所不同,但是大同小异,访问静态成员所用到的函数如下:

/* 获取静态字段的ID */
jfieldID GetStaticFieldID(jclass clazz, const char *name,const char *sig);

/* 获取静态字段的值 */
jobject GetStaticObjectField(jclass clazz, jfieldID fieldID);

/* 设置静态字段的值*/
void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value);


2 程序二:通过字段访问Java类的方法

JNI除了提供字段访问成员的技术之外,还提供了访问对象方法的技术,下面是一个Java对象通过调用Native代码来调用自己成员函数的代码。通过Native调用Java 的Method,并传递一个String,在屏幕打印输出。

2.1 代码

  • Java代码
public class MethodTest{
    static{
        System.loadLibrary("JNITest");
    }

    private void method(String str){
        System.out.println(str);
    }
    private native void JNIMethod();
    public static void main(String args[]){
        MethodTest mt = new MethodTest();
        mt.JNIMethod();
        mt.method("In Java.");
    }
}
  • Native 代码
#include<jni.h>
#include<iostream>
#include<algorithm>

using namespace std;

JNIEXPORT void JNICALL Java_MethodTest_JNIMethod
(JNIEnv*env, jobject obj){
    jclass thiz = env->GetObjectClass(obj);
    jmethodID jmID = env->GetMethodID(thiz, "method", "(Ljava/lang/String;)V");
    const char*utf = "In C";
    jstring jstr = env->NewStringUTF(utf);
    env->CallVoidMethod(obj, jmID,jstr);
}
  • 运行结果
    这里写图片描述

2.2 函数总结

Native访问Java方法,主要通过类的字段来获取方法ID,然后通过该ID搜寻对应的对象,传递参数,调用其所要访问的方法。

jmethodID GetMethodID(jclass clazz, const char *name,const char *sig)

  • jclass clazz 要访问的方法所属的类
  • const char *name 要访问的方法名
  • const char*sig 要访问的方法的类型签名
  • jmethodID 返回访问方法的ID

void CallVoidMethod(jobject obj, jmethodID methodID, ...)

  • jobject obj 调用方法所属对象
  • jmethodID methodID 调用方法的ID
  • ... 可变参数列表,按顺序给调用方法传递参数,注意参数必须是Java支持的类型

2.3 总结和补充

除了一般性的方法之外,对于构造方法,以及父类方法,访问的方式会有所不同,但是也同样大同小异,在此不再罗列。

3 参考链接

[1]. JNI官方规范中文版——如何访问Java中的字段和方法
[2]. JNI学习积累之三 —- 操作JNI函数以及复杂对象传递

猜你喜欢

转载自blog.csdn.net/cv_jason/article/details/79654247