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

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

image.png


在 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等。

目录
相关文章
|
Android开发 开发者
Android自定义view之利用drawArc方法实现动态效果
本文介绍了如何通过Android自定义View实现动态效果,重点使用`drawArc`方法完成圆弧动画。首先通过`onSizeChanged`进行测量,初始化画笔属性,设置圆弧相关参数。核心思路是不断改变圆弧扫过角度`sweepAngle`,并调用`invalidate()`刷新View以实现动态旋转效果。最后附上完整代码与效果图,帮助开发者快速理解并实践这一动画实现方式。
316 0
|
安全 数据库 Android开发
在Android开发中实现两个Intent跳转及数据交换的方法
总结上述内容,在Android开发中,Intent不仅是活动跳转的桥梁,也是两个活动之间进行数据交换的媒介。运用Intent传递数据时需注意数据类型、传输大小限制以及安全性问题的处理,以确保应用的健壯性和安全性。
719 11
|
缓存 Java Shell
Android 系统缓存扫描与清理方法分析
Android 系统缓存从原理探索到实现。
1106 15
Android 系统缓存扫描与清理方法分析
|
ARouter 测试技术 API
Android经典面试题之组件化原理、优缺点、实现方法?
本文介绍了组件化在Android开发中的应用,详细阐述了其原理、优缺点及实现方式,包括模块化、接口编程、依赖注入、路由机制等内容,并提供了具体代码示例。
553 3
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
缓存 NoSQL Java
Redis+Caffeine构建高性能二级缓存
大家好,我是摘星。今天为大家带来的是Redis+Caffeine构建高性能二级缓存,废话不多说直接开始~
1778 0
|
10月前
|
缓存 负载均衡 监控
135_负载均衡:Redis缓存 - 提高缓存命中率的配置与最佳实践
在现代大型语言模型(LLM)部署架构中,缓存系统扮演着至关重要的角色。随着LLM应用规模的不断扩大和用户需求的持续增长,如何构建高效、可靠的缓存架构成为系统性能优化的核心挑战。Redis作为业界领先的内存数据库,因其高性能、丰富的数据结构和灵活的配置选项,已成为LLM部署中首选的缓存解决方案。
918 25
|
11月前
|
存储 缓存 NoSQL
Redis专题-实战篇二-商户查询缓存
本文介绍了缓存的基本概念、应用场景及实现方式,涵盖Redis缓存设计、缓存更新策略、缓存穿透问题及其解决方案。重点讲解了缓存空对象与布隆过滤器的使用,并通过代码示例演示了商铺查询的缓存优化实践。
411 1
Redis专题-实战篇二-商户查询缓存
|
10月前
|
缓存 运维 监控
Redis 7.0 高性能缓存架构设计与优化
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Redis 7.0高性能缓存架构,探索函数化编程、多层缓存、集群优化与分片消息系统,用代码在二进制星河中谱写极客诗篇。
1828 3
|
11月前
|
缓存 NoSQL 关系型数据库
Redis缓存和分布式锁
Redis 是一种高性能的键值存储系统,广泛用于缓存、消息队列和内存数据库。其典型应用包括缓解关系型数据库压力,通过缓存热点数据提高查询效率,支持高并发访问。此外,Redis 还可用于实现分布式锁,解决分布式系统中的资源竞争问题。文章还探讨了缓存的更新策略、缓存穿透与雪崩的解决方案,以及 Redlock 算法等关键技术。