Google的Ndk-Sample学习笔记之一(hello-jniCallback)

前言:  近段时间因为项目的需求,需要使用JNI,所以下载了Google的Ndk-Sample学习下,准备记录 下来,留给后期自己查看

问题点一:JNI_OnLoad方法必须返回JNI的版本

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {JNIEnv *env;memset(&g_ctx, 0, sizeof(g_ctx));g_ctx.javaVM = vm;if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_6) != JNI_OK) {return JNI_ERR;  // JNI version not supported.}jclass clz =(*env)->FindClass(env, "com/example/hellojnicallback/JniHandler");g_ctx.jniHelperClz = (*env)->NewGlobalRef(env, clz);jmethodID jniHelperCtor =(*env)->GetMethodID(env, g_ctx.jniHelperClz, "<init>", "()V");jobject handler = (*env)->NewObject(env, g_ctx.jniHelperClz, jniHelperCtor);g_ctx.jniHelperObj = (*env)->NewGlobalRef(env, handler);queryRuntimeInfo(env, g_ctx.jniHelperObj);g_ctx.done = 0;g_ctx.mainActivityObj = NULL;return JNI_VERSION_1_6;
}

上面是hello-jniCallback项目中的原始文件hello-jnicallback.c中相关代码,为了慢慢学习知识点,我自己没有使用hello-jnicallback.c文件,而是自己创建了自己的学习文件,hello_jni.h 跟hello_jni.c.

根据原始sample的中的文件,大概代码可以简化为如下所示,返回0

jint JNI_OnLoad(JavaVM *vm, void *reserved)
{LOGI("enter JNI_OnLoad");JNIEnv* env;if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_6) != JNI_OK){LOGE("Failed to get GetEnv");return JNI_ERR;}LOGI("leave JNI_OnLoad");return 0;
}

但是运行程序之后,程序发生了错误,通过LOG信息可以发现,错误提示是错误的Jni 版本

估计是这里返回值是要返回一个JNI的对应版本,尝试了下返回JNI_VERSIOn_1_2,JNI_VERSIOn_1_4, JNI_VERSIOn_1_6都没有问题

jint JNI_OnLoad(JavaVM *vm, void *reserved)
{LOGI("enter JNI_OnLoad");JNIEnv* env;......LOGI("leave JNI_OnLoad");return JNI_VERSION_1_2;
}

问题点二:JNI如何调用java中的方法

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {JNIEnv *env;memset(&g_ctx, 0, sizeof(g_ctx));g_ctx.javaVM = vm;if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_6) != JNI_OK) {return JNI_ERR;  // JNI version not supported.}jclass clz =(*env)->FindClass(env, "com/example/hellojnicallback/JniHandler");g_ctx.jniHelperClz = (*env)->NewGlobalRef(env, clz);jmethodID jniHelperCtor =(*env)->GetMethodID(env, g_ctx.jniHelperClz, "<init>", "()V");jobject handler = (*env)->NewObject(env, g_ctx.jniHelperClz, jniHelperCtor);g_ctx.jniHelperObj = (*env)->NewGlobalRef(env, handler);queryRuntimeInfo(env, g_ctx.jniHelperObj);g_ctx.done = 0;g_ctx.mainActivityObj = NULL;return JNI_VERSION_1_6;
}void queryRuntimeInfo(JNIEnv *env, jobject instance) {// Find out which OS we are running on. It does not matter for this app// just to demo how to call static functions.// Our java JniHelper class id and instance are initialized when this// shared lib got loaded, we just directly use them//    static function does not need instance, so we just need to feed//    class and method id to JNIjmethodID versionFunc = (*env)->GetStaticMethodID(env, g_ctx.jniHelperClz, "getBuildVersion", "()Ljava/lang/String;");if (!versionFunc) {LOGE("Failed to retrieve getBuildVersion() methodID @ line %d", __LINE__);return;}jstring buildVersion =(*env)->CallStaticObjectMethod(env, g_ctx.jniHelperClz, versionFunc);const char *version = (*env)->GetStringUTFChars(env, buildVersion, NULL);if (!version) {LOGE("Unable to get version string @ line %d", __LINE__);return;}LOGI("Android Version - %s", version);(*env)->ReleaseStringUTFChars(env, buildVersion, version);// we are called from JNI_OnLoad, so got to release LocalRef to avoid leaking(*env)->DeleteLocalRef(env, buildVersion);// Query available memory size from a non-static public function// we need use an instance of JniHelper class to call JNIjmethodID memFunc = (*env)->GetMethodID(env, g_ctx.jniHelperClz,"getRuntimeMemorySize", "()J");if (!memFunc) {LOGE("Failed to retrieve getRuntimeMemorySize() methodID @ line %d",__LINE__);return;}jlong result = (*env)->CallLongMethod(env, instance, memFunc);LOGI("Runtime free memory size: %" PRId64, result);(void)result;  // silence the compiler warning
}

 java中的JniHandler的相关方法如下

public class JniHandler {private static final String TAG = "hello-jniCallback";/** Print out status to logcat*/@Keepprivate void updateStatus(String msg) {if (msg.toLowerCase().contains("error")) {Log.e("JniHandler", "Native Err: " + msg);} else {Log.i("JniHandler", "Native Msg: " + msg);}}/** Return OS build version: a static function*/@Keepstatic public String getBuildVersion() {return Build.VERSION.RELEASE;}/** Return Java memory info*/@Keeppublic long getRuntimeMemorySize() {return Runtime.getRuntime().freeMemory();}
}

Log信息如下:

通过上面的代码分析:这里有两种方法的调用,一种是CallStaicObjectMethod方法,另外一种是CalllongMethod方法,以为我之前接触过一点JNI的知识,了解JNI跟java的交互分为对象的调用跟类的调用

1.JNI如何调用java静态方法

经过代码分析,大概的调用流程如下所示

  1. findClass
  2. NewGlobalRef
  3. GetMethodId
  4. CallMeehod
jclass clz =(*env)->FindClass(env, "com/example/hellojnicallback/JniHandler");g_ctx.jniHelperClz = (*env)->NewGlobalRef(env, clz);jmethodID jniHelperCtor =(*env)->GetMethodID(env, g_ctx.jniHelperClz, "<init>", "()V");jobject handler = (*env)->NewObject(env, g_ctx.jniHelperClz, jniHelperCtor);g_ctx.jniHelperObj = (*env)->NewGlobalRef(env, handler);jstring buildVersion =(*env)->CallStaticObjectMethod(env, g_ctx.jniHelperClz, versionFunc);const char *version = (*env)->GetStringUTFChars(env, buildVersion, NULL);if (!version) {LOGE("Unable to get version string @ line %d", __LINE__);return;}LOGI("Android Version - %s", version);(*env)->ReleaseStringUTFChars(env, buildVersion, version);// we are called from JNI_OnLoad, so got to release LocalRef to avoid leaking(*env)->DeleteLocalRef(env, buildVersion);

代码修改为如下

jint JNI_OnLoad(JavaVM *vm, void *reserved)
{LOGI("enter JNI_OnLoad");JNIEnv* env;if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_6) != JNI_OK){LOGE("Failed to get GetEnv");return JNI_ERR;}LOGI("leave JNI_OnLoad");jclass clz = (*env)->FindClass(env, "com/example/hellojnicallback/JniHandler");jobject objRef = (*env)->NewGlobalRef(env, clz);jmethodID versionFun = (*env)->GetStaticMethodID(env, objRef, "getBuildVersion", "()Ljava/lang/String;");jstring buildVersion = (*env)->CallStaticObjectMethod(env, objRef, versionFun);const char* version = (*env)->GetStringUTFChars(env, buildVersion, NULL);if (version == NULL){LOGE("Failed to get version");return JNI_ERR;}LOGI("java build version - %s", version);return JNI_VERSION_1_6;
}

经过测试,这样是没有问题的,

但是上面有一个NewGlobalRef的调用通过jni.h的查询发现,jclass跟jobject其实是同一个类型void*

准备测试下,如果不调用这个这个NewGlobalRef能不能正常运行,通过这个方法描述,这个好像只是一个全局的引用,修改为如下所示:

jint JNI_OnLoad(JavaVM *vm, void *reserved)
{LOGI("enter JNI_OnLoad");JNIEnv* env;if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_6) != JNI_OK){LOGE("Failed to get GetEnv");return JNI_ERR;}LOGI("leave JNI_OnLoad");jclass clz = (*env)->FindClass(env, "com/example/hellojnicallback/JniHandler");jmethodID versionFun = (*env)->GetStaticMethodID(env, clz, "getBuildVersion", "()Ljava/lang/String;");jstring buildVersion = (*env)->CallStaticObjectMethod(env, clz, versionFun);const char* version = (*env)->GetStringUTFChars(env, buildVersion, NULL);if (version == NULL){LOGE("Failed to get version");return JNI_ERR;}LOGI("java build version - %s", version);return JNI_VERSION_1_6;
}

经过测试发现,没有任何问题

总结:JNI调用java中的静态方法的流程是

  1. FindClass
  2. GetStaticMethodID
  3. CallStaticobjectMethod

2.JNI如何调用java的对象方法

通过跟上面调用JNI调用静态方法的对比,猜测调用对象方法,应该是一样的

在 java中的JniHandler中添加一个新方法,如下所示

@Keeppublic String getBuildVersion2() {return "BuildVersion2";}

新的代码如下所示

但是发现报错了,错误提示信息如下

JVM object referenced by 'clz' is of type 'Class<JniHandler>' and it does not have access to method 'getBuildVersion2()' declared in class 'JniHandler'. 

猜测是类型不匹配,这里应该是一个jobject类型,而不是jclass类型 ,在上面的步骤中,不是可以通过调用NewGlobalR来获取jobject类型吗,修改如下,

信心满满,这下应该没有问题吧,现实是残酷的,啪啪打脸.c代码发生crash了

 再对比原始的方法,发现有一段这样的代码调用

jmethodID jniHelperCtor =(*env)->GetMethodID(env, g_ctx.jniHelperClz, "<init>", "()V");jobject handler = (*env)->NewObject(env, g_ctx.jniHelperClz, jniHelperCtor);

应该需要通过先调用java的非静态方法,然后再根据生成的jmethodID去调用NewObject.开始编写代码.大概是如下所示

在编写过程中, 在传入参数,versionFun的时候, Android Studio报错了,提示

Not a constructor.  

不是一个构造期,难道需要跟java反射一样吗.需要搞一个构造器,再次去看了下原来的代码,发现有如下几行代码

jmethodID jniHelperCtor =(*env)->GetMethodID(env, g_ctx.jniHelperClz, "<init>", "()V");
jobject handler = (*env)->NewObject(env, g_ctx.jniHelperClz, jniHelperCtor);

根据名字猜测JnihelperCtor,这个难道是需要调用构造器吗,立马行动起来,修改如下

jint JNI_OnLoad(JavaVM *vm, void *reserved)
{LOGI("enter JNI_OnLoad");JNIEnv* env;if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_6) != JNI_OK){LOGE("Failed to get GetEnv");return JNI_ERR;}LOGI("leave JNI_OnLoad");jclass clz = (*env)->FindClass(env, "com/example/hellojnicallback/JniHandler");jmethodID ctor = (*env)->GetMethodID(env, clz,  "<init>", "()V");jobject obj = (*env)->NewObject(env, clz, ctor);jmethodID versionFun = (*env)->GetMethodID(env, clz, "getBuildVersion2", "()Ljava/lang/String;");jstring buildVersion = (*env)->CallObjectMethod(env, obj, versionFun);const char* version = (*env)->GetStringUTFChars(env, buildVersion, NULL);if (version == NULL){LOGE("Failed to get version");return JNI_ERR;}LOGI("java build version - %s", version);return JNI_VERSION_1_6;
}

 运行测试,发现可以了

考虑了下,调用流程应该跟java的反射一样,需要调用构造器函数,调用流程如下

  1. FindClass
  2. GeMethodID(必须是构造器方法)
  3. NewObject
  4. GetMethodID(你想调用的java非静态方法) 
  5. CallObjectMethod

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/616104.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ABAP CDS 常用语法

文章目录 1.什么是abap cdsadap cds的优点 2.常用cds语法常用函数一 数值函数1.绝对值2. 最小整数不小于arg的值3.两个参数相除4.两个数相除切保留小数位5.最小整数不大于arg的值6.取除数的余数 二 字符串函数1.拼接字符2.替换字符3.截取字符串 三 常用的分支语句1.CASE 表达式…

亚马逊API:快速查询全球商品数据的技巧!

了解亚马逊API的限制和要求&#xff1a;在使用亚马逊API之前&#xff0c;您需要了解其限制和要求&#xff0c;例如请求频率限制、认证要求等。确保您遵循了API的使用条款&#xff0c;以避免不必要的麻烦。使用合适的亚马逊API服务&#xff1a;亚马逊提供了多个API服务&#xff…

Linux的SSH密钥认证快捷配置

本文适用&#xff1a;rhel5-9系列&#xff0c;同类系统(CentOS,AlmaLinux,RockyLinux等)、debian系(ubuntu)等也可参照 文档形成时期&#xff1a;2012-2024年 因系统版本不同&#xff0c;配置应略有差异&#xff0c;本文没有在细节上区分&#xff0c;但实践中发现均可配置成功 …

Linux实用命令

文章目录 一.系统与设置命令1. Linux的用户与用户组2.当前用户的详细信息 id3.提高普通用户的权限sudo4.实时显示进程的信息 top5.查看进程信息ps6.杀死进程kill7.关机重启8.显示当前登陆系统的用户who9.校正服务器时间,时区 timedatectl10.清屏命令clear 二.目录管理1.ls列出目…

Atlassian版本选择趋势是上云还是本地部署?全面分析两个版本的特性

近日&#xff0c;龙智联合Atlassian举办的DevSecOps研讨会年终专场”趋势展望与实战探讨&#xff1a;如何打好DevOps基础、赋能创新”在上海圆满落幕。龙智Atlassian技术与顾问咨询团队&#xff0c;以及清晖、JamaSoftware、CloudBees等生态伙伴的嘉宾发表了主题演讲&#xff0…

flutter封装dio请求库,让我们做前端的同学可以轻松上手使用,仿照axios的使用封装

dio是一个非常强大的网络请求库&#xff0c;可以支持发送各种网络请求&#xff0c;就像axios一样灵活强大&#xff0c;但是官网没有做一个demo示例&#xff0c;所以前端同学使用起来还是有点费劲&#xff0c;所以就想在这里封装一下&#xff0c;方便前端同学使用。 官网地址&a…

uniapp开发安卓应用微信开放平台创建应用如何获取签名

微信开放平台创建应用时需要应用的签名 比如我们开发了一个应用叫 “滴滴拉屎” 包名&#xff1a;uni.DIDILASHI #mermaid-svg-BUKbltDr30J93dUs {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-BUKbltDr30J93dUs .…

直播带货2024:洗牌、阵痛和暗流涌动

文 | 螳螂观察 作者 | 青月 一天前&#xff0c;大学生齐夏根本不会在直播间购买《额尔古纳河右岸》这种书籍。 她是喜欢看小说&#xff0c;但只钟爱悬疑无限流题材&#xff0c;至于《额尔古纳河右岸》这种讲述一个弱小民族顽强的抗争和优美的爱情的长篇小说&#xff0c;用齐…

设计模式原理与分析

一、设计模式原理与分析 1、概念 设计模式是在软件开发过程中总结得出的一些可重用的解决方案&#xff0c;它能解决一些实际的问题。一些常见的模式&#xff0c;比如工厂模式、单例模式等等。 封装、继承、多态只是类的三大特性&#xff0c;在程序设计时并不是说使用到了这三个…

读书笔记--微服务架构

微服务架构 服务网关 客户端与微服务直接通信 GateWay&#xff1a;API 网关是一个服务器&#xff0c;是系统的单入口点。它类似于面向对象设计模式中的门面&#xff08;Facade&#xff09;模式。API 网关封装了内部系统架构&#xff0c;并针对每个客户端提供一个定制 API。它…

vue上传文件加进度条,fake-progress一起使用

el-upload上传过程中加进度条&#xff0c;进度条el-progress配合fake-progress一起使用&#xff0c;效果如下&#xff1a; 安装 npm install fake-progress 在用到的文件里面引用 import Fakeprogress from "fake-progress"; 这个进度条主要是假的进度条&#xff…

轻量级图床Imagewheel本地部署并结合内网穿透实现远程访问

文章目录 1.前言2. Imagewheel网站搭建2.1. Imagewheel下载和安装2.2. Imagewheel网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar临时数据隧道3.2.Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测…

springIoc依赖注入循环依赖三级缓存

springIoc的理解&#xff0c;原理和实现 控制反转&#xff1a; 理论思想&#xff0c;原来的对象是由使用者来进行控制&#xff0c;有了spring之后&#xff0c;可以把整个对象交给spring来帮我们进行管理 依赖注入DI&#xff1a; 依赖注入&#xff0c;把对应的属性的值注入到…

全面解读数据安全法规

数据安全&#xff0c;可以说是近些年的热点&#xff0c;特别是随着大数据、人工智能等信息安全技术的快速发展&#xff0c;数据安全和隐私保护形势日益严峻&#xff0c;网络边界被打破&#xff0c;数据安全问题与日俱增。各国也非常重视数据安全建设&#xff0c;如下图展示的全…

js join方法的使用

JavaScript的join()方法是数组对象的方法之一&#xff0c;用于将数组中的所有元素连接成一个字符串。它可以接受一个可选的参数作为分隔符&#xff0c;用于在每个元素之间进行分隔。 以下是join()方法的基本语法&#xff1a; var newString array.join(separator) 在上述方…

设计模式之避免共享的设计模式 Thread-Specific Storage 模式

系列文章目录 设计模式之避免共享的设计模式Immutability&#xff08;不变性&#xff09;模式 设计模式之并发特定场景下的设计模式 Two-phase Termination&#xff08;两阶段终止&#xff09;模式 设计模式之避免共享的设计模式Copy-on-Write模式 提示&#xff1a;写完文章后…

Android Matrix (三)矩阵组合和应用变换

在 Android 开发中&#xff0c;Matrix 类不仅提供了 mapPoints 方法来变换点坐标&#xff0c;还提供了多种其他用法&#xff0c;使其成为处理图像和视图变换的强大工具。以下是 Matrix 类的一些关键用法&#xff1a; 1. 变换方法 setTranslate(float dx, float dy): 设置矩阵…

Android开发基础(二)

Android开发基础&#xff08;二&#xff09; 上篇主要描述了Android系统架构&#xff0c;代码是通过Java表示的&#xff1b; 本篇将从介绍Android组件去理解Android开发&#xff0c;代码将对Java和Kotlin进行对比。 Android组件 Android应用程序由一些零散的有联系的组件组成…

黑洞数(C语言)

黑洞数也称为陷阱数&#xff0c;又称“Kaprekar问题”&#xff0c;是一类具有奇特转换特性的数。 任何一个各位数字不全相同的三位数&#xff0c;经有限次“重排求差”操作&#xff0c;总会得到495。最后所得的495即为三位黑洞数。所谓“重排求差”操作即组成该数的数字重排后…

element input组件自动失去焦点问题解决

最近在 Vue3 ElementPlus 中&#xff0c;使用 el-input 组件时&#xff0c;如果设置了 v-model&#xff0c;那么在每次改变内容后后&#xff0c;input 会自动失去焦点&#xff0c;这样会导致用户无法输入多个字符。 一、问题原因 如上图所示&#xff0c;配置项的 Name 和 Cod…