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.hqwc.cn/news/341583.html

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

相关文章

Elasticsearch 索引文档时create、index、update的区别【学习记录】

本文基于elasticsearch7.3.0版本。 一、思维导图 elasticsearch中create、index、update都可以实现插入功能&#xff0c;但是实现原理并不相同。 二、验证index和create 由上面思维导图可以清晰的看出create、index的大致区别&#xff0c;下面我们来验证下思维导图中的场景&…

FineBI实战项目一(17):热门商品Top10分析开发

点击新建组件&#xff0c;创建热门商品Top10组件。 选择柱状图&#xff0c;拖拽cnt&#xff08;总数&#xff09;到横轴&#xff0c;拖拽goodName到纵轴。 选择排序规则。 修改横轴和纵轴的标签名称 切换到仪表板&#xff0c;拖拽组件到仪表板 效果如下&#xff1a;

【动态规划】 【字典树】C++算法:472 连接词

作者推荐 【动态规划】458:可怜的小猪 涉及知识点 动态规划 字典树 LeetCode472 连接词 给你一个 不含重复 单词的字符串数组 words &#xff0c;请你找出并返回 words 中的所有 连接词 。 连接词 定义为&#xff1a;一个完全由给定数组中的至少两个较短单词&#xff08;不…

[C#]调用tesseact-ocr的traineddata模型进行ocr文字识别

【框架地址】 https://github.com/charlesw/tesseract 【算法介绍】 Tesseract OCR是一个开源的光学字符识别引擎&#xff0c;它可以将图像中的文字转换成可编辑和可搜索的文本格式。Tesseract由惠普实验室于1985年开始开发&#xff0c;并在2005年被Google收购后成为了开源项…

题目:七段码(蓝桥OJ 595)

问题描述&#xff1a; 解题思路&#xff1a; 枚举每一种可能组合&#xff08;可以使用二进制数表示&#xff0c;每一个二进制就是一种组合&#xff09;&#xff0c;在判断是否符合题目要求的每一个发光灯管相邻&#xff08;使用并查集方法确定&#xff0c;当每一个发光…

首个云原生、分布式、全栈国产化银行核心业务系统投产上线丨TiDB × 杭州银行

日前&#xff0c;杭州银行新一代核心业务系统成功投产上线。 新核心系统是业内首个实际投产的云原生、分布式、全栈国产化的银行核心系统&#xff0c;是金融科技领域突破关键核心技术应用的重大实践。 新核心系统自上线以来运行安全稳定&#xff0c;大幅提升了业务处理效率&am…

ptaR7-6/zzuli2106 有去有回

题目 输入n个整数&#xff0c;第一趟按从左到右间隔k个数取数据&#xff0c;然后第二趟再从右到左间隔k-1个数取余下的数&#xff0c;如果数据没有取完&#xff0c;下一趟再间隔k-2个从左到右取数据&#xff0c;如此反复&#xff0c;直到所有的数据取完为止。注意&#xff1a;…

【算法】增减序列(贪心,差分)

题目 给定一个长度为 n 的数列 a1,a2,…,an&#xff0c;每次可以选择一个区间 [l,r]&#xff0c;使下标在这个区间内的数都加一或者都减一。 求至少需要多少次操作才能使数列中的所有数都一样&#xff0c;并求出在保证最少次数的前提下&#xff0c;最终得到的数列可能有多少种…

03 - 系统调用

---- 整理自 王利涛老师 课程 实验环境&#xff1a;宅学部落 www.zhaixue.cc 文章目录 1. 系统调用基本概念1.1 一个系统调用的例子1.2 什么是系统调用&#xff1f;软件复用的角度 2. 软中断&#xff1a;系统调用的入口2.1 权限管理2.2 系统调用号2.4 man 2 syscall2.5 实验&am…

t2vec code

文章目录 执行过程preprocess.jl 解释h5 文件结构 执行过程 (base) zzqserver1:~/project/t2vec/preprocessing$ julia porto2h5.jl Processing 1710660 trips… 100000 200000 300000 400000 500000 600000 700000 800000 900000 1000000 1100000 1200000 1300000 1400000 15…

HiDataPlus 3.3.2-005 搭建(个人的一点心得体会 x86 平台)

HDP 集群搭建 前置安装 yum -y install createrepo yum install -y lrzsz yum install -y wget yum install -y vim修改当前集群机器的主机名 hostnamectl set-hostname XXX​ 这里的 XXX 就是要设置的当前机器的主机名称。主机名称是集群唯一的&#xff0c;一定不要重复&am…

解决“百度网盘启动缓慢”问题

最近在使用百度网盘&#xff0c;双击桌面的《百度网盘》图标&#xff0c;发现有等好几分钟&#xff0c;软件才会启动。百度网盘启动太慢了&#xff0c;后面发现百度网盘&#xff0c;使用dll注入技术&#xff0c;附加到很多不相干的进程里&#xff0c;比如附加explorer进程、附加…