Android NDK开发系列教程3:基本方法调用及传参(续)

简介: 终于建了一个自己个人小站:https://huangtianyu.gitee.io,以后优先更新小站博客,欢迎进站,O(∩_∩)O~~ 上一节主要讲解Java向native传参,下面主要讲解从native传相应的数据到java层。

终于建了一个自己个人小站:https://huangtianyu.gitee.io,以后优先更新小站博客,欢迎进站,O(∩_∩)O~~
上一节主要讲解Java向native传参,下面主要讲解从native传相应的数据到java层
接着上一节,下面主要讲解内容如下:
1. native向java返回字符串类型
2. native向java返回java对象
3. native向java返回数组类型
4. native向Java返回List对象
对于上面的每个都给出对应的例子。
本节所有案例代码均已放到GitHub上,欢迎下载:
https://github.com/huangtianyu/JNILearnCourse

1. native向java返回字符串类型

传基本数据类型很简单,是什么就传什么就行。传字符串类型也很简单,具体jni代码如下:

extern "C"
JNIEXPORT jstring JNICALL
Java_zqc_com_example_NativeTest_jni2javaMethod1(JNIEnv *env, jobject instance) {
    //jstring NewStringUTF(const char* bytes),jstring NewString(const jchar* unicodeChars, jsize len)
    char *returnValue = "你在native做你的操作后,生成char*后,通过env->NewStringUTF即可返回Java的String类型";
    return env->NewStringUTF(returnValue);
}

其中最主要用的是以下几个方法:

    //创建Unicode格式的jstring串
    jstring NewString(const jchar* unicodeChars, jsize len)
    { return functions->NewString(this, unicodeChars, len); }
    //获取jstring长度
    jsize GetStringLength(jstring string)
    { return functions->GetStringLength(this, string); }
    //获取jstring对应的字符串,isCopy表示是否拷贝生成副本。
    //这个函数返回一个指向特定jstring中字符顺序的指针,该指针保持有效直到ReleaseStringChars函数被调用:
    const jchar* GetStringChars(jstring string, jboolean* isCopy)
    { return functions->GetStringChars(this, string, isCopy); }
    //释放指针
    void ReleaseStringChars(jstring string, const jchar* chars)
    { functions->ReleaseStringChars(this, string, chars); }
    ////创建UTF-8格式的jstring串
    jstring NewStringUTF(const char* bytes)
    { return functions->NewStringUTF(this, bytes); }
    //获取utf字符串的长度
    jsize GetStringUTFLength(jstring string)
    { return functions->GetStringUTFLength(this, string); }
    //同GetStringChars
    const char* GetStringUTFChars(jstring string, jboolean* isCopy)
    { return functions->GetStringUTFChars(this, string, isCopy); }
    //同ReleaseStringChars
    void ReleaseStringUTFChars(jstring string, const char* utf)
    { functions->ReleaseStringUTFChars(this, string, utf); }

以上是处理字符串常用的一些方法。

2 native向java返回java对象

具体看native的代码如下:

extern "C"
JNIEXPORT jobject JNICALL
Java_zqc_com_example_NativeTest_jni2javaMethod2(JNIEnv *env, jobject instance) {
    jclass pcls = env->FindClass("zqc/com/example/Person");
    jmethodID constructor = env->GetMethodID(pcls, "<init>", "()V");
    jmethodID setIdMid = env->GetMethodID(pcls, "setId", "(J)V");
    jmethodID setNameMid = env->GetMethodID(pcls, "setName", "(Ljava/lang/String;)V");
    jmethodID setAgeMid = env->GetMethodID(pcls, "setName", "(I)V");
    jobject person = env->NewObject(pcls, constructor);
    env->CallVoidMethod(person, setIdMid, 100L);
    env->CallVoidMethod(person, setNameMid, env->NewStringUTF("天宇"));
    env->CallVoidMethod(person, setAgeMid, 18);
    return person;
}

常用新建Object的方法由以下几个:

    //将传递给构造函数的所有参数紧跟着放在 methodID 参数的后面。NewObject() 收到这些参数后,将把它们传给所要调用的Java 方法。
    jobject NewObject(jclass clazz, jmethodID methodID, ...)
    {
        va_list args;
        va_start(args, methodID);
        jobject result = functions->NewObjectV(this, clazz, methodID, args);
        va_end(args);
        return result;
    }
    //将传递给构造函数的所有参数放在 va_list 类型的参数 args 中,该参数紧跟着放在 methodID 参数的后面。NewObject() 收到这些参数后,将把它们传给所要调用的 Java 方法。
    jobject NewObjectV(jclass clazz, jmethodID methodID, va_list args)
    { return functions->NewObjectV(this, clazz, methodID, args); }
    //将传递给构造函数的所有参数放在jvalues类型的数组args中,该数组紧跟着放在methodID参数的后面。NewObject() 收到数组中的这些参数后,将把它们传给所要调用的 Java 方法。
    jobject NewObjectA(jclass clazz, jmethodID methodID, jvalue* args)
    { return functions->NewObjectA(this, clazz, methodID, args); }
    //测试对象是否为某个类的实例。
    jboolean IsInstanceOf(jobject obj, jclass clazz)
    { return functions->IsInstanceOf(this, obj, clazz); }
    //测试两个引用是否引用同一 Java 对象。
    jboolean IsSameObject(jobject ref1, jobject ref2)
    { return functions->IsSameObject(this, ref1, ref2); }
    //分配新 Java 对象而不调用该对象的任何构造函数。返回该对象的引用。
    //该方法会抛出:InstantiationException:如果该类为一个接口或抽象类。OutOfMemoryError:如果系统内存不足。
    jobject AllocObject(jclass clazz)
    { return functions->AllocObject(this, clazz); }
    //判断某个Object是否是某个class的具体实例
    jboolean    (*IsInstanceOf)(JNIEnv*, jobject, jclass);

3 native向java返回数组类型

3.1 基本类型数组

这里直接看native层代码如下:

extern "C"
JNIEXPORT jintArray JNICALL
Java_zqc_com_example_NativeTest_jni2javaMethod3(JNIEnv *env, jobject instance) {
    int nat[] = {2, 1, 4, 3, 5};
    jintArray jnat = env->NewIntArray(5);
    env->SetIntArrayRegion(jnat, 0, 5, nat);
    return jnat;
}

基本数据类型数组都有相应的env->NewXXXArray(jsize length);通过该方法可以生成对应的数组。

jbooleanArray NewBooleanArray(jsize length)
    { return functions->NewBooleanArray(this, length); }
    jbyteArray NewByteArray(jsize length)
    { return functions->NewByteArray(this, length); }
    jcharArray NewCharArray(jsize length)
    { return functions->NewCharArray(this, length); }
    jshortArray NewShortArray(jsize length)
    { return functions->NewShortArray(this, length); }
    jintArray NewIntArray(jsize length)
    { return functions->NewIntArray(this, length); }
    jlongArray NewLongArray(jsize length)
    { return functions->NewLongArray(this, length); }
    jfloatArray NewFloatArray(jsize length)
    { return functions->NewFloatArray(this, length); }
    jdoubleArray NewDoubleArray(jsize length)
    { return functions->NewDoubleArray(this, length); }

在生成了对应的数组后,可以通过setXXXArrayRegion(jxxxArray array, jsize start, jsize len, const jchar* buf)来填充数组

    void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len,
        const jboolean* buf)
    { functions->SetBooleanArrayRegion(this, array, start, len, buf); }
    void SetByteArrayRegion(jbyteArray array, jsize start, jsize len,
        const jbyte* buf)
    { functions->SetByteArrayRegion(this, array, start, len, buf); }
    void SetCharArrayRegion(jcharArray array, jsize start, jsize len,
        const jchar* buf)
    { functions->SetCharArrayRegion(this, array, start, len, buf); }
    void SetShortArrayRegion(jshortArray array, jsize start, jsize len,
        const jshort* buf)
    { functions->SetShortArrayRegion(this, array, start, len, buf); }
    void SetIntArrayRegion(jintArray array, jsize start, jsize len,
        const jint* buf)
    { functions->SetIntArrayRegion(this, array, start, len, buf); }
    void SetLongArrayRegion(jlongArray array, jsize start, jsize len,
        const jlong* buf)
    { functions->SetLongArrayRegion(this, array, start, len, buf); }
    void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len,
        const jfloat* buf)
    { functions->SetFloatArrayRegion(this, array, start, len, buf); }
    void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len,
        const jdouble* buf)
    { functions->SetDoubleArrayRegion(this, array, start, len, buf); }
3.2 对象类型数组

直接看native代码:

extern "C"
JNIEXPORT jobjectArray JNICALL
Java_zqc_com_example_NativeTest_jni2javaMethod4(JNIEnv *env, jobject instance) {
    jclass cls = env->FindClass("zqc/com/example/Person");
    jmethodID cMid = env->GetMethodID(cls, "<init>", "()V");
    jclass pcls = env->FindClass("zqc/com/example/Person");
    jmethodID cmid = env->GetMethodID(pcls, "<init>", "()V");
    jmethodID setNameMid = env->GetMethodID(pcls, "setName", "(Ljava/lang/String;)V");
    jmethodID setAgeMid = env->GetMethodID(pcls, "setAge", "(I)V");
    jobject obj = env->NewObject(pcls, cmid);
    env->CallVoidMethod(obj, setNameMid, env->NewStringUTF("天宇"));

    int len = 3;
    jobjectArray joa = env->NewObjectArray(len, cls, obj);
    for (int i = 0; i < len; ++i) {
        jobject tmp = env->GetObjectArrayElement(joa,i);
        env->CallVoidMethod(tmp, setAgeMid, i + 10);
    }
    return joa;
}

其在native生成的方法是 jobjectArray joa = env->NewObjectArray(len, cls, obj);

//第一个参数表示生成的长度,第二参数表示里面元素的对象类,第三个表示原始初始化时的值。在生成后每个元素都是该值。
jobjectArray NewObjectArray(jsize length, jclass elementClass,
        jobject initialElement)
    { return functions->NewObjectArray(this, length, elementClass,
        initialElement); }

4 native向Java返回List对象

直接看native代码如下:

extern "C"
JNIEXPORT jobject JNICALL
Java_zqc_com_example_NativeTest_jni2javaMethod5(JNIEnv *env, jobject instance) {
    jclass listCls = env->FindClass("java/util/ArrayList");//获得ArrayList类引用
    jmethodID  listCon = env->GetMethodID(listCls, "<init>", "()V");//获取构造函数的methodID
    jmethodID addMid = env->GetMethodID(listCls,"add","(Ljava/lang/Object;)Z");//获取add函数的methodID

    jobject listObj = env->NewObject(listCls, listCon);//利用NewObject创建一个ArrayList对象
    jobject jperon = Java_zqc_com_example_NativeTest_jni2javaMethod2(env, instance);//利用上面方法新建一个Person对象
    env->CallBooleanMethod(listObj, addMid, jperon);//在listObj中add一个Person对象
    //返回ArrayList的对象
    return listObj;
}

对应jni而言,List,ArrayList以及Map,HashMap,Set,HashSet都只是一个Object,对应于jni而言也就都是jobject,操作jobject都可以用最开始介绍的方法。

总结

jni里面的方法很多,多用用就熟悉了。常用的上面都有,自己之前还总结了很多常用的类型转换函数。以后有时间再写篇博客分享下。不会就要查手册:http://www.ceeger.com/Script/AndroidJNI/AndroidJNI.html

目录
相关文章
|
4天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
24 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
27天前
|
Java Android开发
Android 开发获取通知栏权限时会出现两个应用图标
Android 开发获取通知栏权限时会出现两个应用图标
14 0
|
1月前
|
XML 缓存 Android开发
Android开发,使用kotlin学习多媒体功能(详细)
Android开发,使用kotlin学习多媒体功能(详细)
103 0
|
1月前
|
设计模式 人工智能 开发工具
安卓应用开发:构建未来移动体验
【2月更文挑战第17天】 随着智能手机的普及和移动互联网技术的不断进步,安卓应用开发已成为一个热门领域。本文将深入探讨安卓平台的应用开发流程、关键技术以及未来发展趋势。通过分析安卓系统的架构、开发工具和框架,本文旨在为开发者提供全面的技术指导,帮助他们构建高效、创新的移动应用,以满足不断变化的市场需求。
18 1
|
1月前
|
机器学习/深度学习 调度 Android开发
安卓应用开发:打造高效通知管理系统
【2月更文挑战第14天】 在移动操作系统中,通知管理是影响用户体验的关键因素之一。本文将探讨如何在安卓平台上构建一个高效的通知管理系统,包括服务、频道和通知的优化策略。我们将讨论最新的安卓开发工具和技术,以及如何通过这些工具提高通知的可见性和用户互动性,同时确保不会对用户造成干扰。
33 1
|
1天前
|
数据库 Android开发 开发者
安卓应用开发:构建高效用户界面的策略
【4月更文挑战第24天】 在竞争激烈的移动应用市场中,一个流畅且响应迅速的用户界面(UI)是吸引和保留用户的关键。针对安卓平台,开发者面临着多样化的设备和系统版本,这增加了构建高效UI的复杂性。本文将深入分析安卓平台上构建高效用户界面的最佳实践,包括布局优化、资源管理和绘制性能的考量,旨在为开发者提供实用的技术指南,帮助他们创建更流畅的用户体验。
|
18天前
|
XML 开发工具 Android开发
构建高效的安卓应用:使用Jetpack Compose优化UI开发
【4月更文挑战第7天】 随着Android开发不断进化,开发者面临着提高应用性能与简化UI构建流程的双重挑战。本文将探讨如何使用Jetpack Compose这一现代UI工具包来优化安卓应用的开发流程,并提升用户界面的流畅性与一致性。通过介绍Jetpack Compose的核心概念、与传统方法的区别以及实际集成步骤,我们旨在提供一种高效且可靠的解决方案,以帮助开发者构建响应迅速且用户体验优良的安卓应用。
|
21天前
|
监控 算法 Android开发
安卓应用开发:打造高效启动流程
【4月更文挑战第5天】 在移动应用的世界中,用户的第一印象至关重要。特别是对于安卓应用而言,启动时间是用户体验的关键指标之一。本文将深入探讨如何优化安卓应用的启动流程,从而减少启动时间,提升用户满意度。我们将从分析应用启动流程的各个阶段入手,提出一系列实用的技术策略,包括代码层面的优化、资源加载的管理以及异步初始化等,帮助开发者构建快速响应的安卓应用。
|
21天前
|
Java Android开发
Android开发之使用OpenGL实现翻书动画
本文讲述了如何使用OpenGL实现更平滑、逼真的电子书翻页动画,以解决传统贝塞尔曲线方法存在的卡顿和阴影问题。作者分享了一个改造后的外国代码示例,提供了从前往后和从后往前的翻页效果动图。文章附带了`GlTurnActivity`的Java代码片段,展示如何加载和显示书籍图片。完整工程代码可在作者的GitHub找到:https://github.com/aqi00/note/tree/master/ExmOpenGL。
22 1
Android开发之使用OpenGL实现翻书动画
|
21天前
|
Android开发 开发者
Android开发之OpenGL的画笔工具GL10
这篇文章简述了OpenGL通过GL10进行三维图形绘制,强调颜色取值范围为0.0到1.0,背景和画笔颜色设置方法;介绍了三维坐标系及与之相关的旋转、平移和缩放操作;最后探讨了坐标矩阵变换,包括设置绘图区域、调整镜头参数和改变观测方位。示例代码展示了如何使用这些方法创建简单的三维立方体。
18 1
Android开发之OpenGL的画笔工具GL10