Android C++系列:JNI 调用时缓存字段和方法 ID

news/2024/7/8 15:33:41 标签: android, c++, 缓存

在 JNI 去调用 Java 的方法和访问字段时,最先要做的操作就是获得对应的类以及对应的方法 id。

通常我们通过 FindClass 、GetFieldID、GetMethodID 去找到对应的信息也是耗时操作,如果方法被频繁调用(特别是像音视频处理时循环的调用JNI方法传递音视频数据),每次都去查找对应的类和方法ID会很耗性能,所以我们必须将它们缓存起来,达到只创建一次,后面直接使用缓存内容的效果。

缓存有两种方式,分别是使用时缓存和初始化时缓存

使用时缓存

使用时缓存,就是在调用时查找一次,然后将它缓存成 static静态变量,这样下次调用时直接使用即可。直到内存释放了,才会缓存失效。

extern "C"
 JNIEXPORT void JNICALL
 Java_com_qingkouwei_demo_CacheFieldAndMethodDemo_staticCacheField(JNIEnv *env, jobject instance, jobject fruit) {
     static jfieldID fid = NULL; // 声明为 static 变量进行缓存
     // 两种方法都行
 //    jclass cls = env->GetObjectClass(animal);
     jclass cls = env->FindClass("com/qingkouwei/demo/bean/Fruit");
     jstring jstr;
     const char *c_str;
     // 从缓存中查找
     if (fid == NULL) {
         fid = env->GetFieldID(cls, "name", "Ljava/lang/String;");
         if (fid == NULL) {
             return;
         }
     } else {
         LOGD("field id is cached");
     }
     jstr = (jstring) env->GetObjectField(fruit, fid);
     c_str = env->GetStringUTFChars(jstr, NULL);
     if (c_str == NULL) {
         return;
     }
     env->ReleaseStringUTFChars(jstr, c_str);
     jstr = env->NewStringUTF("new name");
     if (jstr == NULL) {
         return;
     }
     env->SetObjectField(animal, fid, jstr);
 }

通过声明为 static 变量进行缓存的方式显然有弊端,当多个调用者同时调用时,就会出现缓存多次的情况,并且每次调用时都要检查是否缓存过了。这种情况我们可以用下一种方式,做一个初始化方法,初始化时缓存一次。

初始化时缓存

在初始化时缓存,就是在类加载时,进行缓存。当类被加载进内存时,会先调用类的静态代码块,所以可以在类的静态代码块中进行缓存

比如:

public class CacheFieldAndMethodDemo {
     
     static {
         init(); // 静态代码块中进行缓存
     }
     private static native void init();
 }

在静态代码块中,可以将所需要的字段 id 或者方法 id 缓存成全局变量。

具体代码如下:

// 全局变量,作为缓存方法 id
 jmethodID InstanceMethodCache;
 // 初始化加载时缓存方法 id
 extern "C"
 JNIEXPORT void JNICALL
 Java_com_qingkouwei_demo_CacheFieldAndMethodDemo_init(JNIEnv *env, jclass type) {
     jclass cls = env->FindClass("com/qingkouwei/demo/bean/Fruit");
     InstanceMethodCache = env->GetMethodID(cls, "getName", "()Ljava/lang/String;");
 }

在 JNI 中直接将方法 id 缓存成全局变量了,这样再调用时,就不要再进行一次查找了,并且避免了多个线程同时调用会多次查找的情况。

extern "C"
 JNIEXPORT void JNICALL
 Java_com_qingkouwei_demo_CacheFieldAndMethodDemo_callCacheMethod(JNIEnv *env, jobject instance, jobject animal) {
     jstring name = (jstring) env->CallObjectMethod(animal, InstanceMethodCache);
     const char *c_name = env->GetStringUTFChars(name, NULL);
     LOGD("call cache method and value is %s", c_name);
 }

总结

之前分享的JNI操作是基础,基于应用到实战中就要讲究各种技巧,用以提升效率。本文提到JNI调用时字段和方法ID缓存其实最终的方案还是以初始化时缓存为主,是在实际开发中总结出来的。

之前在Android平台封装了lamemp3、opus等音频编码器,都是使用线程的C语言实现的编码器,然后在Java层利用AudioRecorder录音,然后将音频数据通过JNI调用到这些编码器编码方法,这些接口的主要流程是:

  1. 创建编码器;
  2. 循环调用编码器编码方法,输入pcm数据,输出编码后音频数据;
  3. 释放编码器。

这里面就可以把创建编码器充当初始化方法来获取编码方法的方法id等。


http://www.niftyadmin.cn/n/5537369.html

相关文章

三、数据库系统(考点篇)

1、三级模式一两级映像 内模式:管理如何存储物理的 数据 ,对数据的存储方式、优化、存放等。 模式:又称为概念模式, 就是我们通常使用的表这个级别 ,根据应用、需求将物理数据划分成一 张张表。 外模式:…

模拟5亿年自然进化史,全新蛋白质大模型ESM3诞生!前Meta老将力作LeCun转赞

模拟5亿年自然进化史,全新蛋白质大模型ESM3诞生!前Meta老将力作LeCun转赞。 能抗衡AlphaFold 3的生命科学大模型终于出现了。初创公司Evolutionary Scale AI发布了他们最新的98B参数蛋白质语言模型ESM3。不仅支持序列、结构、功能的all-to-all推理&#…

强化学习的数学原理:蒙特卡洛学习

概述 实际上将之前 policy iteration 当中基于模型的部分只要替换掉不需要模型的部分,就得到了本节课的蒙特卡洛的算法;另外在本门课当中将 value iteration 和 policy iteration 统称为 model-based reinforcement learning,更准确的说应该…

如何通过KB知识库系统实现内部知识的管理

“Baklib 通过构建KB知识库系统实现内部知识的管理,构建 CMS 系统实现网站内容管理,构建 DAM 实现对原子化数字内容的管理。” Baklib 从多个维度和深度实现对数字内容的管理。 CMS 系统 CMS 系统(Content Management System 内容管理系统)是一种帮助用…

前端面试题2(vue3)

1. Vue 3 中的 Composition API 是什么?与 Options API 的区别是什么? 答案: Composition API 是 Vue 3 引入的一种新的编程模型,它允许开发者以更灵活和模块化的方式组织代码。与传统的 Options API 相比,Compositio…

大量设备如何集中远程运维?用好向日葵的这几个功能很重要

当企业的业务不断发展,不同系统、不同用途的IT设备数量也会不断上升,面对不断扩张的设备规模,IT运维的压力自然也会陡然上升。 面对这种情况,传统的线下运维方式已经不再合适,我们需要引入一个专业的,可以…

拍摄泡咖啡的剧本!

泡咖啡的过程可以是一种放松和享受的仪式,同时也是一个记录生活美好瞬间的好机会。以下是一些步骤和提示,帮助您记录泡咖啡的过程: 1. **准备材料**: 确保您有新鲜的咖啡豆、磨豆机、咖啡壶、滤纸、热水和杯子。 2. **选择音乐**&…

2024 Parallels Desktop for Mac 功能介绍

Parallels Desktop的简介 Parallels Desktop是一款由Parallels公司开发的桌面虚拟化软件,它允许用户在Mac上运行Windows和其他操作系统。通过强大的技术支持,用户无需重新启动电脑即可在Mac上运行Windows应用程序,实现了真正的无缝切换。 二…