NDK-Java与native互相传递复杂对象

/ 0评 / 5

前言

在前几篇博客中分别介绍了Java与native层方法互相调用的问题,接下来本篇博客将会介绍如何在Java以及native之间传递复杂参数(对象)。

在native中返回一个复杂对象

首先我们简单的写一个Student类,实现无参构造函数以及有参构造函数。具体如下。

public class Student {

    private static final String TAG = "Student";

    private int age = -1;

    private String name ="undefine name";

    public Student() {
        Log.i(TAG, "Student: 无参构造函数");
    }

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
        Log.i(TAG, "Student: age = " + age + ",name = " + name);
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

在Java中,我们想实例化一个对象只需要使用new即可,如Student stu = new Student();这样会调用Student的无参构造函数实例化出一个对象,那么我们想在native中实例化一个对象就只需要调用对应对象的构造方法即可。在上一篇中我介绍了native如何调用Java方法的步骤,首先获取对应的jclass对象,然后获取jmethodID,这样我们就能直接调用构造方法了。

获取jmethodID需要使用方法签名,Java构造函数的签名为<init>,返回值为void

下面这个例子为直接调用无参构造函数

Java_org_ndk_ndkfirst_NDKTest_getStudentFromNative(JNIEnv *env, jobject obj) {
    jclass stuClass = env->FindClass("org/ndk/ndkfirst/Student");
    //构造函数的名字为<init> 返回值为void
    jmethodID stuconstrocID = env->GetMethodID(stuClass, "<init>", "()V");
    //调用无参构造函数
    jobject student = env->NewObject(stuClass, stuconstrocID);
    return student;
}

下面这个例子为调用有参数的构造函数

JNIEXPORT jobject JNICALL
Java_org_ndk_ndkfirst_NDKTest_getStudentFromNative2(JNIEnv *env, jobject obj) {
    //获取Student的jclass对象
    jclass stuCls = env->FindClass("org/ndk/ndkfirst/Student");
    //调用有参数的构造函数,参数为int,String
    jmethodID stuid = env->GetMethodID(stuCls, "<init>", "(ILjava/lang/String;)V");
    //参数为10,native student
    return env->NewObject(stuCls, stuid, 10, env->NewStringUTF("native student"));
}

完整代码请查看文末给出的链接。

Java中传递一个复杂对象给native

为了测试是否传递成功,我们需要让native能直接打印log到logcat中,要想实现这个功能,我们需要使用#include "android/log.h"包含日志输出的头文件。具体步骤如下。

1、让生成的so文件中包含系统的日志输出模块。

对于这一点,我们直接仿照系统的CMakeLists.txt设置一下即可。

add_library( # Sets the name of the library.
             ndk-test-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/ndk_test.cpp
             )


find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

target_link_libraries( # Specifies the target library.
                       ndk-test-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

2、包含头文件,并且定义相应的宏函数

#include "android/log.h"

#define TAG "ndk-jni" // 这个是自定义的LOG的标识
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型

3、输出log

LOGI("native output : %s",env->GetStringUTFChars(str,NULL));
//等同于如下
Log.i(TAG, "native output : ");

CMakeLists.txt的其他配置可以查看NDK-CMake初探

JNIEXPORT void JNICALL
Java_org_ndk_ndkfirst_NDKTest_outputStudentInNative(JNIEnv *env, jobject obj, jobject stu) {
    //获取类对象
    jclass stuCls = env->GetObjectClass(stu);
    //调用toString方法
    jmethodID toStringid = env->GetMethodID(stuCls, "toString", "()Ljava/lang/String;");
    //调用方法
    jstring str = (jstring) env->CallObjectMethod(stu, toStringid);
    //这里需要使用GetStringUTFChars而不是GetStringChars,因为jstring为宽字符
    LOGI("native output : %s", env->GetStringUTFChars(str, NULL));
}

为了节省便宜,对于很多基本代码进行了省略,具体的可以查看下面的Demo链接。

Demo地址:github

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注