转载自搜狗测试公众号,本人学习使用,侵权删

最近小编在做公司输入法项目中java与native交互部分的测试,先简单学习了java代码调用native代码的实现原理,本次与大家一起分享jni协议,了解java关联C/C++代码的调用原则。

JNI是Java Native Interface的缩写,能够提供API实现Java和Native语言(主要是C/C++)的通信,JNI提供两种方式实现Java对native代码的调用:静态关联和动态关联。

静态关联

静态关联的实现过程是通过经过特定规则命名的jni函数名来遍历java和jni函数之间的关联。具体分三步实现:

1、java代码中声明native函数;

2、通过javah生成native函数的jni形式;

3、在jni代码中实现native函数。

示例如下:

1、实现一段java代码JNIUtils.java:

package com.example.administrator.myapplication;

public class JNIUtils {    
   static{
       System.loadLibrary("native-lib");
   }    
   public static native String sayHiFromJNI();
}

JNIUtils.java代码包名为com.example.administrator.myapplication,声明了native函数名为sayHiFromJNI()。

2、通过javah生成native函数的jni形式

在代码的src/main/java目录下通过terminal端输入命令:javah -d ../jni com.example.administrator.myapplication.JNIUtils。通过Javah命令能够生成java类对应的头文件,命令-d表示生成一个目录,习惯上我们会将jni相关代码存放在java同级目录下的jni文件夹中(../jni),最后的com.example.administrator.myapplication.JNIUtils就是我们的JNIUtils完整类名了。

执行后jni目录下会生成一个com.example.administrator.myapplication.JNIUtils.h文件,如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_administrator_myapplication_JNIUtils */ #ifndef _Included_com_example_administrator_myapplication_JNIUtils
#define _Included_com_example_administrator_myapplication_JNIUtils
#ifdef __cplusplus
extern "C" {
#endif/*
* Class:     com_example_administrator_myapplication_JNIUtils
* Method:    sayHiFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_administrator_myapplication_JNIUtils_sayHiFromJNI
 (JNIEnv *, jclass); #ifdef __cplusplus
}
#endif
#endif

3、在jni代码中实现native函数

在jni目录中新建cpp文件,命名JNIHi.cpp,在cpp文件中include "com_example_administrator_myapplication_JNIUtils.h"实现native函数的功能即可,在JNIUtils.java文件中我们定义了public static native String sayHiFromJNI();函数,因此在JNIHi.cpp中需要实现具体逻辑。

代码如下:

#include "com_example_administrator_myapplication_JNIUtils.h"

JNIEXPORT jstring JNICALL Java_com_example_administrator_myapplication_JNIUtils_sayHiFromJNI        
       (JNIEnv *env, jclass jclass){    
   return env->NewStringUTF("Hi From JNI!!!");
}

如此便实现了JNIUtils.java代码中对C++代码JNIHi.cpp中函数的调用。

动态关联

静态关联的方法简单易学,但是是不是有人觉得函数名这么长,规范是否太繁琐,那么我们还有更简单的方式:动态关联。

动态方式的主要实现原理是通过RegisterNatives函数把C/C++中的方法映射到Java中。

1、编写java代码JNIUtils.java,与静态关联相同

package com.example.administrator.myapplication;

public class JNIUtils {    
   static{
       System.loadLibrary("native-lib");
   }    
   public static native String sayHiFromJNI();
}

上述函数中我们使用System.loadLibrary("native-lib")方法加载so库的时候,Java虚拟机就会找到JNI_OnLoad函数并调用,该函数前面有三个关键字分别是JNIEXPORT,JNICALL ,jint。其中JNIEXPORT和JNICALL是两个宏定义,用于指定该函数是JNI函数,通过该函数能够实现java与native的动态关联,以代码示例。

2、编写native关联代码JNIHi.cpp

代码示例:

#include <jni.h>
#include <stdio.h>
#include<android/log.h>
#include <stdlib.h> using namespace std;
#ifdef __cplusplus
extern "C" {
#endif static const char *className = "com/example/administrator/myapplication/JNIUtils"; JNIEXPORT jstring JNICALL sayHiFromJNI(JNIEnv *env,jobject obj) {    
   return env->NewStringUTF("Hi From JNI!!!");
} static JNINativeMethod gJni_Methods_table[] = {
       {"sayHiFromJNI", "()Ljava/lang/String;", (void*)sayHiFromJNI},
}; JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){    JNIEnv* env = NULL;
   jint result = -1;    
   if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {        
       return result;
   }    jclass clazz = (env)->FindClass( className);    
   if (clazz == NULL){        
       return -1;
   }    
   
   if ((env)->RegisterNatives(clazz, gJni_Methods_table, sizeof(gJni_Methods_table) / sizeof(gJni_Methods_table[0])) < 0)
   {        
       return -1;
   }    
   
   return JNI_VERSION_1_4;
} #ifdef __cplusplus
}
#endif

通过代码阅读,我们发现JNI_OnLoad函数的实现主要包含两步:第一、vm->GetEnv()函数获取JNIEnv结构体指针,该指针指向一个函数表,对应JNI函数,我们可以通过这些JNI函数实现JNI编程;第二、RegisterNatives()函数实现native方法的注册,其中主要应用了一个静态变量JNINativeMethod类型的数组,它代表了native方法。JNINativeMethod结构被定义在jni.h中,Java与JNI可以通过该结构建立联系,如此Java虚拟机就可以用相应的函数映射表来调用相应的函数,而不需要通过函数名来查找需要调用的函数了。

小结

简而言之,静态关联:先由Java声明本地方法,然后通过JNI实现方法的定义。动态关联:先通过JNI_OnLoad实现本地方法,然后直接在Java中调用。两种方法各有优缺点,大家根据自己的代码习惯选择合适的方式就好。

Android项目中实现native调用的更多相关文章

  1. Android项目中JNI技术生成并调用.so动态库实现详解

    生成 jni方式有两种:一种是通过SWIG从C++代码生成过度的java代码:另一种是通过javah的方式从java代码自动生成过度的C++代码.两种方式下的步骤流程正好相反. 第一种方式:由于需要配 ...

  2. Android 项目中文件夹的说明与作用(转)

    (转自:http://blog.csdn.net/goodshot/article/details/11529731) Android 项目中文件夹的作用 1. src:存放所有的*.java源程序. ...

  3. android项目中如何加载已有so库 <转>

    1,在项目根目录下建立文件夹libs/armeabi文件夹 2,将so库放入 libs/armeabi文件夹 注意事项: 1,如果采用静态注册的方式请注意C文件中严格按照命名规则 Java_packa ...

  4. android项目中刷新activity界面

    android项目中在sqlite数据库插入/更新/删除数据后: 1. 刷新当前activity界面数据(手动刷新): 在activity类下新增一个refresh()方法: /** * 刷新, 这样 ...

  5. 在Android项目中使用AspectJ

    版权声明:本文为博主原创文章,未经博主允许不得转载. 转载请表明出处:http://www.cnblogs.com/cavalier-/p/8888459.html 什么是AOP AOP是 Aspec ...

  6. android项目中配置NDK自动编译生成so文件

    1 下载ndk开发包   2 在android 项目中配置编译器(以HelloJni项目为例)  2.1 创建builer  (a)Project->Properties->Builder ...

  7. android XMl 解析神奇xstream 一: 解析android项目中 asset 文件夹 下的 aa.xml 文件

    简介 XStream 是一个开源项目,一套简单实用的类库,用于序列化对象与 XML 对象之间的相互转换. 将 XML 文件内容解析为一个对象或将一个对象序列化为 XML 文件. 1.下载工具 xstr ...

  8. 对Android项目中的文件夹进行解释

    对Android项目中的文件夹进行解释: · src:里面存放的是Activity程序,或者是以后的其他组件,在此文件夹之中建立类的时候一定要注意,包名称不能是一级. · gen:此文件夹中的内容是自 ...

  9. 在Android项目中启用Java 8的部分特性--Lambda & Method References

    Android N发布时同时发布了一个新的编译工具Jack(AS2.1+支持),基于Jack我们可以使用Java 8 的部分特性,在低版本机器上能使用的更少,同时Jack也有诸多不完善,工具链的改变难 ...

随机推荐

  1. 三步完成Source Insight 4.0 破解安装(转)

    转自:https://blog.csdn.net/biubiuibiu/article/details/78044232 三步完成Source Insight 4.0 破解安装   下载地址有更新,之 ...

  2. 用List和Map排序输出

    参考:java的treemap反序输出 int->string string->int java对象数组的概述 List import java.io.*; import java.uti ...

  3. Tomcat是否关闭 maxEntriesLocalHeap

    EHCache does not allow attribute "maxEntriesLocalHeap". 这个错误是由于这个属性不支持2.5以下版本 故更新ehcache版本 ...

  4. 【持久层】Druid简介

    Druid首先是一个数据库连接池.Druid是目前最好的数据库连接池,在功能.性能.扩展性方面,都超过其他数据库连接池,包括DBCP.C3P0.BoneCP.Proxool.JBoss DataSou ...

  5. PCB一些设置记录

    开始时设置原点,编辑>>原点>>设置 画PCB时,导入后,根据各个模块放好位置 设计>>类>>添加电源类 设计>>规则>>Cle ...

  6. hdu 4826 Labyrinth(简单dp)

    Description 度度熊是一只喜欢探险的熊,一次偶然落进了一个m*n矩阵的迷宫,该迷宫只能从矩阵左上角第一个方格开始走,只有走到右上角的第一个格子才算走出迷宫,每一次只能走一格,且只能向上向下向 ...

  7. Tomcat负载均衡图片显示不正常解决方法

    在部署一个Tomcat玩玩的时候,发现在做nginx负载均衡时,网站显示不正常,图片会变得很大.测试了半天都没成功,最后查找资料,才发现Tomcat负载均衡时Session处理有问题,Session是 ...

  8. Service系统服务(五):PXE基础装机环境、配置并验证DHCP服务、配置PXE引导、验证PXE网络装机、PXE+kickstart自动装机

    一.PXE基础装机环境 目标: 本例要求为后续的PXE服务器构建提供RHEL7软件仓库,完成下列任务: 1> 在CentOS真机部署Web目录/var/www/html/rh7dvd   2&g ...

  9. [CSP-S模拟测试]:影子(并查集+LCA)

    题目描述 一个人有很多的影子,新的旧的,他们不断消失重来.学者的影子在他苍白色的精神图景里成为了$n$个黑色的点,他们伸长的触手交叉形成了一颗黑色的树.假使每个影子点拥有一个权值$d_i$,黑色的树边 ...

  10. (转)使用openGL显示图像(一)建立OpenGL ES的环境

    编写:jdneo - 原文:http://developer.android.com/training/graphics/opengl/environment.html 要在应用中使用OpenGL E ...