前言
在前几篇博客中分别介绍了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