JNI相关笔记

1 生成native code所需要的头文件

  • 首先使用javac对java文件进行编译
  • 使用javah -jni [className],生成对应的头文件
  • 创建cpp文件,实现所需要的函数
  • 运行方式:java -Djava.library.path=. HelloWorld或者export LD_LIBRARY_PATH

2 JNI提供的一些函数和方法

  • String对象的获取

    • 获取java传下来的String对象:const jbyte* byteString = (*env)->GetStringUTFChars(env,str,NULL);,由于该函数是对原来字符串的拷贝,所以使用完后需要对byteString进行NULL判断

      使用完毕后,需要释放获取的对象:(*env)->ReleaseStringUTFChars(env,str,byteString);

    • GetStringRegion/GetStringUTFRegion:会将字符串拷贝到预先申请的一片缓冲区里面(在栈上面,因此应避免大对象的拷贝),所以当使用这两个函数的时候,不需要进行NULL判断,也不需要进行Release操作

    • GetStringCritical/ReleaseStringCritical:会尽可能直接使用String的指针(也有可能返回字符串的拷贝),且该方法会阻塞垃圾回收,因此使用的时候需当做临界资源,即在这两个函数中间不能使用JNI调用或者阻塞

  • 类对象的获取

    • 获取当前类的实例:GetObjectClass:jclass cls = (*env)->GetObjectClass(env, obj)

      获取和设置类中成员变量的方法:

        jfieldID fid;
      fid = (*env)->GetFieldID(env, cls, "s", "Ljava/lang/String;");
      //cls为上一步获取的类的实例,GetStaticFieldID获取静态成员变量
      if(!fid) {
      //需要判断是否获取成功
      return; /* failed to find the field !*/
      }
      jstring str = (*env)->GetObjectField(env, obj, fid); //获取jstring对象,获取完后就可以转换为const char*类型来操作了
      jstring target = (*env)->NewStringUTF(env, "123");
      if(!jstr) {
      return; /*out of memory */
      }
      (*env)->SetObjectField(env, obj, fid, jstr); //设置对应的field

    调用java类中的方法:

        jclass cls = (*env)->GetObjectClass(env, obj);
    jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "()V");
    if(!mid) {
    return; /* method not found */
    }
    printf("In C\n");
    //直接调用java对象中的方法,对应的还有CallIntMethod, CallStaticVoidMethod, CallStaticBooleanMethod
    (*env)->CallVoidMethod(env, obj, mid);

3 局部引用,全局引用,全局弱引用。

局部应用的错误示例:https://www.kancloud.cn/owenoranba/jni/120497 以为用static变量来存储findClass的结果,实际上由于findClass返回的局部引用,该引用指向的对象可能已经被销毁,所以这样的做法是无用的

正确的做法是用返回的局部引用创建一个GlobalRef:https://www.kancloud.cn/owenoranba/jni/120498

一般来讲局部引用会自动进行释放,但是这种情况下,最好手动释放局部引用:

for (int i = 0; i < len; ++i) {
jstring jstr = (*env)->GetObjectArrayElement(env, arr, i);
/* ... process jstr */
(*env)->DeleteLocalRef(env, jstr);
}

IsSameObject(jobject obj1,jobject obj2):用来判断两个object是否引用的同一个对象,对于弱全局引用非常有效

EnsureLocalCapacity(jint capacity):确保native函数在调用前有资源能够创建至少capacity个局部引用

(*env)->PushLocalFrame(env, 10)和result = (*env)->PopLocalFrame(env, result):管理局部引用更高效的方式,示例:https://www.kancloud.cn/owenoranba/jni/120505

4 异常

当native代码调用java类中的方法的时候,有时候java类中可能会抛出异常,一般的检测方式为:

(*env)->CallVoidMethod(env, obj, mid);
//ExceptionCheck,更为高效的异常检查方式,返回值为boolean,只关心是否发生异常,不关心异常类的引用
exc = (*env)->ExceptionOccurred(env);
if(exc){
jclass newExcCls;
(*env)->ExceptionDescribe(env);
//打印出异常的描述信息
(*env)->ExceptionClear(env);//需要清理掉异常,为了下一次能够检测到
//省略部分代码
}

对于本地代码来讲,如果需要抛出异常,那么方式为:

jclass newExcCls;
newExcCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
if(!newExcCls){
/*Unable to find the exception class, give up. */
return;
} (*env)->ThrowNew(env, newExcCls, "thrown from C code"); //抛出异常一个非常重要的一点,对于native代码来讲,ThrowNew方法并不会中断代码流程,所以需要手动去控制代码流程一个用于方便抛出异常的工具函数:
void JNU_ThrowByName(JNIEnv *env, const char* name, const char* msg){
jclass cls = (*env)->FindClass(env, name);
/*if cls is NULL, an exception has already been thrown */
if(cls){
(*env)->ThrowNew(env, cls, msg);
}
/* free the local ref */
(*env)->DeleteLocalRef(env, cls);//从此处也可以看出来,native代码中的ThrowNew是不影响流程的,需要自行控制
}

JNI相关笔记 [TOC]的更多相关文章

  1. HTTPS证书申请相关笔记

    申请免费的HTTPS证书相关资料 参考资料: HTTPS 检测 苹果ATS检测 什么是ECC证书? 渠道2: Let's Encrypt 优点 缺点 Let's Encrypt 的是否支持非80,44 ...

  2. Android NDK JNI 入门笔记-day04-NDK实现Hash算法

    * Android NDK JNI 入门笔记目录 * 开头 前面的学习,我们已经掌握了 NDK 开发的必备知识. 下一步就要多实践,通过创造问题并解决问题,来增加熟练度,提升经验. 日常开发中,经常会 ...

  3. JNI学习笔记_Java调用C —— Android中使用的方法

    一.笔记 1.JNI(Java Native Interface),就是如何使用java去访问C/C++编写的那些库.若想深入了解JNI可以看官方文档jni.pdf.优秀博文:Android JNI知 ...

  4. JNI 学习笔记

    JNI是Java Native Interface的缩写,JNI是一种机制,有了它就可以在java程序中调用其他native代码,或者使native代码调用java层的代码.也 就是说,有了JNI我们 ...

  5. JNI学习笔记_Java调用C —— 非Android中使用的方法

    一.学习笔记 1.java源码中的JNI函数本机方法声明必须使用native修饰. 2.相对反编译 Java 的 class 字节码文件来说,反汇编.so动态库来分析程序的逻辑要复杂得多,为了应用的安 ...

  6. JNI学习笔记_C调用Java

    一.笔记 1.C调用Java中的方法,参考jni.pdf pg97可以参考博文:http://blog.csdn.net/lhzjj/article/details/26470999步骤: a. 创建 ...

  7. Hadoop相关笔记

    一.            Zookeeper( 分布式协调服务框架 ) 1.    Zookeeper概述和集群搭建: (1)       Zookeeper概述: Zookeeper 是一个分布式 ...

  8. redis相关笔记(二.集群配置及使用)

    redis笔记一 redis笔记二 redis笔记三 1.配置:在原redis-sentinel文件夹中添加{8337,8338,8339,8340}文件夹,且复制原8333中的配置 在上述8333配 ...

  9. redis相关笔记(三.redis设计与实现(笔记))

    redis笔记一 redis笔记二 redis笔记三 1.数据结构 1.1.简单动态字符串: 其属性有int len:长度,int free:空闲长度,char[] bur:字符数组(内容) 获取字符 ...

随机推荐

  1. 这次我好像才真的明白了CSS Rem字体计算的原理

    背景 如何按照设计稿中标注的尺寸,直接写页面的样式,不再需要px2rem这样的工具或者人工转换 ? 只要你明白了rem的计算原理,这个问题的答案超级简单. 根字体大小计算核心原理 设备的根字体大小 * ...

  2. HTML中的JavaScript

    HTML中的JavaScript 1.<script>元素 defer:可选.表示脚本可以延迟到文档完全被解析和显示之后再执行.只对外部脚本文件有效. 脚本会被延迟到整个页面都解析完毕后再 ...

  3. 依赖倒置原则(Dependence Inversion Principle)

    目录 背景 说明 例子 "倒置"的解释 总结 参考资料 背景 这几天组内的人一起学习DDD,里面再次提到了依赖倒置原则,在这学习过程中,大家又讨论了一下依赖倒置原则. 说明 采用依 ...

  4. CRM的未来发展前景有哪些?

    随着时代的发展,近年来越来越多的国内中小企业开始采用CRM客户关系管理系统,CRM从此不再是大企业的专利,也开始让中小企业得以不断成长.国内CRM行业的发展越来越快, 它的前景是什么?今天小Z就来给大 ...

  5. [并发编程 - 多线程:信号量、死锁与递归锁、时间Event、定时器Timer、线程队列、GIL锁]

    [并发编程 - 多线程:信号量.死锁与递归锁.时间Event.定时器Timer.线程队列.GIL锁] 信号量 信号量Semaphore:管理一个内置的计数器 每当调用acquire()时内置计数器-1 ...

  6. [设计模式] 设计模式课程(十二)-- 门面模式(Facade)

    概述 也称外观模式 按目的属于结构型模式,按封装属于接口隔离模式 在组件构建过程中,某些接口之间的依赖常常会带来很多问题.甚至根本无法实现.采用添加一层间接(稳定)接口,来隔离本来互相紧密关联的接口 ...

  7. [BD] Storm

    什么是实时计算 离线计算:批处理,代表MapReduce.Spark Core,采集数据Sqoop.Flume 实时计算:源源不断,代表Storm等,采集数据Flume 框架 Apache Storm ...

  8. Fedora镜像下载地址

    Fedora镜像下载地址 Fedora 7核心源码包在: http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/7/F ...

  9. 003.kubernets对于namespace的管理

    一 Kuberbetes的架构简单介绍 1.1 云计算的传统分类 1.2 kubernetes基础架构 工作机制 用户通过kubectl向api-server提交需要运行的pod描述 api-serv ...

  10. Centos7环境初始化

    最近在做公司的一个环境搭建的任务的时候,要用到三台Centos7服务器,在上面要预装java1.8,docker,zookeeper并且要在docker中跑一个mysql,还要部署其他的软件.由于不是 ...