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,一经查实,立即删除!

相关文章

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

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

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;用齐…

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;如下图展示的全…

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应用程序由一些零散的有联系的组件组成…

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

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

信息收集之子域名收集

渗透测试常见手法和思维 信息收集 简述 信息收集对于渗透测试前期来说是非常重要的&#xff0c;因为只有我们掌握了目标网站或目标主机足够多的信息之后&#xff0c;我们才能更好地对其进行漏洞检测。正所谓&#xff0c;知己知彼百战百胜&#xff01; 信息收集的方式可以分…

linux下can调试工具canutils编译安装

命令安装只需要 sudo apt-get install canutils 一、下载源码 下载canutils和libsocketcan libsocketcan地址&#xff1a;https://public.pengutronix.de/software/libsocketcan/libsocketcan-0.0.11.tar.bz2 #0.0.11版本 canutils地址&#xff1a;https://public.pengutronix…

【计算机毕业设计】基于springboot的校园跑腿任务管理系统java+vue

校园跑腿管理系统又称“效率鸭”跑腿系统&#xff0c;是在学校进行现代化的信息管理和提供信息服务的基础&#xff0c;引导人们快速、准确地获取快递资源、预约洗浴并对外卖资源进行有效管理的保证。疫情当下&#xff0c;为了减少人员的聚集&#xff0c;因此&#xff0c;迫切需…

索引策略-多列索引

一. 前言 当我们对多列索引的理解不够深刻的时候,往往会为每个列创建独立的索引或者按照错误的顺序创建多列索引。 二. 案例说明 问题一: 为每个列创建独立索引 CREATE TABLE t(c1 INT,c2 INT,c3 INT,KEY(c1),KEY(c2),KEY(c3) );这种索引策略,一般都是由于人们听到一些专家诸…

阳光保险选择OceanBase稳定运行超700天

阳光保险集团成立于 2005 年 7 月&#xff0c;旗下拥有财产保险、人寿保险、信用保证保险、资产管理等多家专业子公司&#xff0c;是全球市场化企业中成长最快的集团公司之一&#xff0c;目前位列中国保险行业前八。随着数字化升级趋势的不断加速&#xff0c;很多企业产生将软硬…

达摩研究院Paraformer-large模型已支持windows

简介 FunASR是一个基础语音识别工具包&#xff0c;提供多种功能&#xff0c;包括语音识别&#xff08;ASR&#xff09;、语音端点检测&#xff08;VAD&#xff09;、标点恢复、语言模型、说话人验证、说话人分离和多人对话语音识别等。FunASR提供了便捷的脚本和教程&#xff0…

二十三、关于vite项目中无法使用minio的解决方案

问题背景 项目需要上传大文件,既然是大文件,如果一次性进行读取发送、接收都是不可取的,很容易导致内存问题。所以对于大文件上传,就一定要实现切片上传、断点续传。如果自己实现相对比较麻烦,但好消息是我们的文件服务使用了开源的minio作为对象存储服务,并且minio也提…

OpenGL排坑指南—贴图纹理绑定和使用

一、前言 在OpenGL学习 的纹理这一章中讲述了纹理贴图的使用方式&#xff0c;主要步骤是先创建一个纹理的对象&#xff0c;和创建顶点VAO类似&#xff0c;然后就开始绑定这个纹理&#xff0c;最后在循环中使用&#xff0c;有时候可能还要用到激活纹理单元的函数。然而&#xff…