栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

RN通信原理 -- Java&C++实现通信

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

RN通信原理 -- Java&C++实现通信

本篇文章中,我们来看一下Java与C++的相互调用是如何完成的。接下来我们分两种情况展开。

一、 Java调用C++

整体流程如下:

    编写带有native关键字修饰的方法的Java类

    使用javac工具编译Java类

    使用javah生成与native修饰的方法对应的(.h)头文件

    使用C++ 实现头文件并编译为so文件

    编译运行完成调用

步骤一 : 编写带有native关键字修饰的方法的Java类

public class Sample {
    //声明四种类型的native方法
    public native int intMethod(int n);
    public native boolean booleanMethod(boolean bool);
    public native String stringMethod(String text);
    public native int intArrayMethod(int[] intArray);
    public static void main(String[] args) {
        //将Sample.so动态类库,加载到当前进程中
        System.loadLibrary("Sample");
        Sample sample = new Sample();
        //调用native方法
        int square = sample.intMethod(5);
        boolean bool = sample.booleanMethod(true);
        String text = sample.stringMethod("Java");
        int sum = sample.intArrayMethod(new int[]{1,2,3,4,5,8,13});
        //打印得到的值
        System.out.println("intMethod: " + square);
        System.out.println("booleanMethod: " + bool);
        System.out.println("stringMethod: " + text);
        System.out.println("intArrayMethod: " + sum);
    }
}

步骤二 : 将Java文件编译生成class文件

jimmy@58deMacBook-Pro-9 ~> javac Sample.java

步骤三 : 生成头文件

>javah Sample

生成的头文件代码如下:

#include 
#ifndef _Included_Sample
#define _Included_Sample

#ifdef __cplusplus
extern "C" {
#endif


JNIEXPORT jint JNICALL Java_Sample_intMethod(JNIEnv *, jobject, jint);


JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod(JNIEnv *, jobject, jboolean);


JNIEXPORT jstring JNICALL Java_Sample1_stringMethod(JNIEnv *, jobject, jstring);


JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod(JNIEnv *, jobject, jintArray);

#ifdef __cplusplus
}
#endif

#endif

方法签名Signature类型详表:

表头java类型Signature备注
booleanZ-
byteB
charC
shortS
intI
longL
floatF
doubleD
voidV
objectL用/分割的完整类名例如: Ljava/lang/String表示String类型
Array[签名例如: [I表示int数组, [Ljava/lang/String表示String数组
Method(参数签名)返回类型签名例如: ([I)I表示参数类型为int数组, 返回int类型的方法

步骤四 : C++ 实现头文件中的函数

#include "Sample.h"
#include 

JNIEXPORT jint JNICALL Java_Sample_intMethod(JNIEnv *env, jobject obj, jint num){
    return num * num;
}

JNIEXPORT jboolean JNICALL Java_Sample_booleanMethod(JNIEnv *env, jobject obj, jboolean boolean){
    return !boolean;
}

JNIEXPORT jstring JNICALL Java_Sample_stringMethod(JNIEnv *env, jobject obj, jstring string){
    const char* str = env->GetStringUTFChars(string, 0);
    char cap[128];
    strcpy(cap, str);
    env->ReleaseStringUTFChars(string, 0);
    return env->NewStringUTF(strupr(cap));
}

JNIEXPORT jint JNICALL Java_Sample_intArrayMethod(JNIEnv *env, jobject obj, jintArray array){
    int i, sum = 0;
    jsize len = env->GetArrayLength(array);
    jint *body = env->GetIntArrayElements(array, 0);
    for (i = 0; i < len; ++i){
        sum += body[i];
    }
    env->ReleaseIntArrayElements(array, body, 0);
    return sum;
}

env.GetStringUTFChars()是用来在Java和C之间转换字符串的, 因为Java本身都使用了双字节的字符, 而C语言本身都是单字节的字符, 所以需要进行转换.

每个函数都有 JNIEnv * 参数, 它包含了很多有用的方法, 使用起来类似Java中的反射。

GetStringUTFChars() 和 NewStringUTF(), 第一个是从UTF8转换为C的编码格式, 第二个是根据C的字符串返回一个UTF8字符串.

ReleaseStringUTFChars()是用来释放对象的, 在Java中虚拟机是有一个垃圾回收机制的, 但是在C语言中, 这些对象必须手动回收. 否则可能造成内存泄漏.

C 和 C++ 的实现非常相似, 只有一个不同点:

C代码: (*env)->GetStringUTFChars(env, string, 0);

C++ 代码: env->GetStringUTFChars(string, 0);

C语言中使用的是结构体的函数指针, 而在C++ 中使用的是struct。

步骤五 : 编译运行完成调用

步骤一的Java文件中已经完成了Java对Native的调用,需要注意的是在调用之前,首先需要将so动态类库加载到当前进行。

二、C++调用Java

该方式的实现需要用到的功能如下:

    创建虚拟机

    寻找class对象, 创建对象

    调用静态方法和成员方法

    获取成员属性, 修改成员属性

该方式实现的步骤:

实现的大体流程与Java调C++ 的基本一致。唯一不同的是,C++ 调用Java的方式,需要在C++ 类中通过类似Java反射的方式来完成对Java的调用。

步骤一 : 编写Java代码

public class Sample {
    public String name;

    public static String sayHello(String name) {
        return "Hello, " + name + "!";
    }

    public String sayHello() {
        return "Hello, " + name + "!";
    }
}

步骤二 : 编译生成class文件

>javac Sample.java

步骤三 : 编写C++ 代码并完成对Java函数的调用

#include 
#include 
#include 
int main(void){
    //虚拟机创建所需的相关参数
    JavaVMOption options[1]; //相当于在命令行里传入的参数
    JNIEnv *env; //JNI环境变量
    JavaVM *jvm; //虚拟机实例
    JavaVMInitArgs vm_args; //虚拟机创建的初始化参数, 这个参数里面会包含JavaVMOption
    long status; //虚拟机启动是否成功的状态


    jclass cls; //将要寻找的Class对象
    jmethodID mid; //Class对象中的方法Id
    jfieldID fid; //Class对象中的属性Id
    jobject obj; //创建的新对象


    //创建虚拟机
    // "-Djava.class.path=."是JVM将要寻找并加载的 .class文件路径
    options[0].optionString = "-Djava.class.path=."; 
    memset(&vm_args, 0, sizeof(vm_args));
    vm_args.version = JNI_VERSION_1_4; //vm_args.version是Java的版本
    vm_args.nOptions = 1; //vm_args.nOptions是传入的参数的长度
    vm_args.options = options; //把JavaVMOption传给JavaVMInitArgs里面去.


    //启动虚拟机
    status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
    if (status != JNI_ERR){
        // 首先获得class对象(JVM在Java中都是自己启动的, 但在C++ 中只能手动启动, 启动完之后的事情就和在Java中一样了, 不过要使用C++ 的语法)。
        cls = (*env)->FindClass(env, "Sample2");
        if (cls != 0){
            // 获取方法ID, 通过方法名和签名, 调用静态方法
            mid = (*env)->GetStaticMethodID(env, cls, "sayHello", "(Ljava/lang/String;)Ljava/lang/String;");
            if (mid != 0){
                const char* name = "World";
                jstring arg = (*env)->NewStringUTF(env, name);
                jstring result = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid, arg);
                const char* str = (*env)->GetStringUTFChars(env, result, 0);
                printf("Result of sayHello: %sn", str);
                (*env)->ReleaseStringUTFChars(env, result, 0);
            }


            

            // 获取属性ID, 通过属性名和签名
            fid = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");
            if (fid != 0){
                const char* name = "icejoywoo";
                jstring arg = (*env)->NewStringUTF(env, name);
                (*env)->SetObjectField(env, obj, fid, arg); // 修改属性
            }
        
            // 调用成员方法
            mid = (*env)->GetMethodID(env, cls, "sayHello", "()Ljava/lang/String;");
            if (mid != 0){
                jstring result = (jstring)(*env)->CallObjectMethod(env, obj, mid);
                const char* str = (*env)->GetStringUTFChars(env, result, 0);
                printf("Result of sayHello: %sn", str);
                (*env)->ReleaseStringUTFChars(env, result, 0);
            }

            //我们可以看到静态方法是只需要class对象, 不需要实例的, 而非静态方法需要使用我们之前实例化的对象.
        }

        //执行完操作之后,销毁虚拟机
        (*jvm)->DestroyJavaVM(jvm);
        return 0;
    } else{
        printf("JVM Created failed!n");
        return -1;
    }
}

额外补充知识:java的String使用了unicode, 是双字节的字符, 而C/C++中使用的单字节的字符.

    从C转换为java的字符, 使用NewStringUTF方法:

jstring arg = (*env)->NewStringUTF(env, name);

    从java转换为C的字符, 使用GetStringUTFChars

const char* str = (*env)->GetStringUTFChars(env, result, 0);

步骤四 : 编译运行

编译运行,完成 C++对Java的调用。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/711697.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号