目前接触的JNI有java调用c和c调用java两类。其中java调用c又有隐式和显示两种映射关系。本笔记针对java调用c的显示映射。本着工程实际够用的原则,不够再回头来补充。
JAVA访问c库需要有三个步骤
1:加载C库
2:建立java函数名到c库函数名的映射关系
3:在java程序里调用函数
先贴代码,依照代码来做分析:

public class JNIDemo {static {        /* 1. load */System.loadLibrary("native"); /* libnative.so */}public native void hello();public static void main (String args[]) {JNIDemo d = new JNIDemo();      /* 2. map java hello <-->c c_hello *//* 3. call */d.hello();}
}

分条缕析做笔记:

  • 1:加载C库和在java程序里调用函数
static {        /* 1. load */System.loadLibrary("native"); /* libnative.so */}

代码中在静态代码块中进行加载c库。可以知道我们要加载的是libnative.so库。放到静态代码块中可以保证该加载能够只执行一次。非常棒的处理。

public native void hello();

native修饰的hello表明这个hello方法不是在java中实现的而是在本地语言中实现的。我们在使用hello函数的时候首先要进行声明。这个声明也可以声明为静态的,比如public native static void hello();这时候就没有必要先实现JNIDemo的实例化静态对象了,也就是说我们可以省掉JNIDemo d=new JNI Demo();这个处理,直接使用JNIDemo.hello();当然我们的main函数也是在JNIDemo类中的,可以直接写为hello()。问题又来了,那么同样的道理为什么不是静态的还需要实现JNIDemo对象呢,这个也是在JNIDemo的main函数中啊?这个就没必要较真了,记住就好,又不是让你去创造java语言,你只不过是用这个工具而已。我还发现这个public native static void hello()跟public static native void hello()是一个效果。无碍乎这个static跟native的前后。到此我们对这个加载c库就算是掌握了。

  • 2:建立java函数名到c库函数名的映射关系
    为了将java中的hello跟c语言中的函数对应起来,这部分的处理是c语言中做的。我们姑且猜测,c语言在什么时候去做这个映射对应呢?我提出一个想法,我们在加载SystemloadLibrary的时候会引起c库文件中的一个触发,在那个触发函数里面做了对应。那么我们从文件中的结构应该是包含 触发 实现。我们贴代码分析:

#include <jni.h>  /* /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ */
#include <stdio.h>#if 0
typedef struct {char *name;          /* Java里调用的函数名 */char *signature;    /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */void *fnPtr;          /* C语言实现的本地函数 */
} JNINativeMethod;
#endifvoid c_hello(JNIEnv *env, jobject cls)
{printf("Hello, world!\n");
}static const JNINativeMethod methods[] = {{"hello", "()V", (void *)c_hello},
};/* System.loadLibrary */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{JNIEnv *env;jclass cls;if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {return JNI_ERR; /* JNI version not supported */}cls = (*env)->FindClass(env, "JNIDemo");if (cls == NULL) {return JNI_ERR;}/* 2. map java hello <-->c c_hello */if ((*env)->RegisterNatives(env, cls, methods, 1) < 0)return JNI_ERR;return JNI_VERSION_1_4;
}

我们看到,果不其然跟我们猜测的一模一样。
的确是分为两部分。最顶层那部分实现了c_hello函数,最下半部分实现了一个JNI_OnLoad触发函数。哇咔咔,一切都这么顺滑。那我们开垦这个触发函数吧。
该函数功能比较单一所以写法肯定是固定的。每次使用我们只需要去修改这么几个点就可以了:
JNI_VERSION_1_4 这个代表的是你使用的jni的版本。

    cls = (*env)->FindClass(env, "JNIDemo");

这个作用就是从env找到JNIDemo这个类,方面后面我们去做映射。故而这个JNIDemo我们做移植的时候是需要去修改的。

    if ((*env)->RegisterNatives(env, cls, methods, 1) < 0)

这个地方也就是我们想要的注册了。后面的1代表你要注册的个数。
我们再看看这个注册methods里面的实现。

static const JNINativeMethod methods[] = {{"hello", "()V", (void *)c_hello},
};

这个模式也是固定的
其中“hello”对应的是java中的函数名字。c_hello对应的c文件中的函数名字。前面的void * 是永远不变的。中间的这个类型signature我是打死也不想记住怎么写的。我们可以在写完java文件之后使用javac JNIDemo.java进行编译然后使用
javah -jni JNIDemo
生成一个JNIDemo.h文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JNIDemo */#ifndef _Included_JNIDemo
#define _Included_JNIDemo
#ifdef __cplusplus
extern "C" {
#endif
/** Class:     JNIDemo* Method:    hello* Signature: ()V*/
JNIEXPORT void JNICALL Java_JNIDemo_hello(JNIEnv *, jclass);#ifdef __cplusplus
}
#endif
#endif

我们从里面摘取

/** Class:     JNIDemo* Method:    hello* Signature: ()V*/

就知道这个signature 就是()V.
同时我们知道我们的c文件的hello函数书写形式应该类似与

JNIEXPORT void JNICALL Java_JNIDemo_hello(JNIEnv *, jclass);

实际中我们的c语言函数书写是

void c_hello(JNIEnv *env, jobject cls)

几乎是一样的,其中jobject 跟jclass我觉得应该是java编译器不同导致的,我感性的认为应该是兼容的,都能用。既然我的编译器出来的是jclass那么我的代码就修改为

void c_hello(JNIEnv *env, jclass cls)

写到这里,还剩下两件事,怎样编译c文件为lib*.so以及java同c语言文件如何传递返回数据类型。我们知道java跟c的数据类型上是有区别的。包括开辟的空间大小等。最起码的java中有string类型,然而我们的c语言中就没有。因此,这部分还算是一个不大不小的知识点。按下不表下集再说。

JAVA进阶day07JNI(java调用c)A部分相关推荐

  1. JAVA进阶day07JNI(java调用c)B部分

    本博文要做两件事 1:怎样将c文件编译成lib*.so 2:java同c语言文件如何传递返回数据类型 现在开始搞起: 1:怎样将c文件编译成lib*.so Linux下编译共享库时,必须加上-fPIC ...

  2. [java进阶]1.Java读取txt文件和写入txt文件

    1. Java读取txt文件 import java.io.*; import java.util.ArrayList; import java.util.List;public class unit ...

  3. Java进阶:java程序设计慕课版课后答案浪潮优派

    摘要 Spring框架自诞生来就备受开发者青睐,很多开发者一致认为它是目前最屌的Java项目,正是因为它的"过人之处",市面上99%的企业都在使用Spring框架. 所以,Spri ...

  4. 【Java进阶】java函数式编程的使用

    目录 1.目前Java中自带的函数式编程接口 2.java中使用函数式编程的案例 3.自定义函数式接口 4.自定义函数式接口的实现 简单一句话理解函数式编程,传统的方法调用我们都是传递参数,而函数式编 ...

  5. 【JAVA进阶】java中的集合(番外篇3)- HashMap源码底层数据结构分析

    写在前面的话 脑子是个好东西,可惜的是一直没有搞懂脑子的内存删除机制是什么,所以啊,入行多年,零零散散的文章看了无数,却总是学习了很多也忘了很多. 痛定思痛的我决定从今天开始系统的梳理下知识架构,记录 ...

  6. Java进阶:java字符串定位语句

    正文 模块(Module).组件(Component).包(Package),这些概念对于我们技术同学并不陌生,但并不是所有人都能理解其要义. 深入理解之后,我才发现,其背后的深意是分类思维.而这种分 ...

  7. Java进阶(七)Java加密技术之非对称加密算法RSA

    Java加密技术(四)--非对称加密算法RSA 非对称加密算法--RSA 基本概念 非对称加密算法是一种密钥的保密方法. 非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(priv ...

  8. 【Java进阶】Java并发类库提供的线程池有哪几种? 分别有什么特点?

    我在专栏第 17 讲中介绍过线程是不能够重复启动的,创建或销毁线程存在一定的开销,所以利用线程池技术来提高系统资源利用效率,并简化线程管理,已经是非常成熟的选择. 今天我要问你的问题是,Java 并发 ...

  9. Java进阶(五)Java I/O模型从BIO到NIO和Reactor模式

    本文介绍了Java中的四种I/O模型,同步阻塞,同步非阻塞,多路复用,异步阻塞.同时将NIO和BIO进行了对比,并详细分析了基于NIO的Reactor模式,包括经典单线程模型以及多线程模式和多Reac ...

最新文章

  1. python基础考核试题及答案
  2. 子视图屏蔽父视图手势
  3. JavaScript | 数据属性与访问器属性
  4. [tp5] thinkPHP5-渲染模板的方式
  5. MPEG-4 压缩编码标准
  6. 认识Blend 3工作区
  7. 一种RTP接收和解包的程序
  8. [Java并发编程实战] 简介
  9. js隐藏打开项目隐藏编辑 和删除按钮
  10. 如何使用Power BI和R脚本创建高级分析
  11. 开源游戏《一小时人生》GitHub仓库被删,CEO亲自道歉
  12. linux技巧33条
  13. C#复习(学生信息输入)
  14. MATLAB实现滚动密钥密码
  15. 驱动精灵w8ndows xp sp2,惠普打印机驱动官方正式版下载,适用于winxp,win2003,winvista,win7,win8,win10,win2008,win2012-驱动精灵...
  16. 秃头真的是程序员的标配吗
  17. linux篇—Nginx反向代理负载均衡
  18. python求根公式_python与代数
  19. miniGUI-4.0.2 交叉编译
  20. 小米6MIUI稳定版安装谷歌相机

热门文章

  1. 大数据技术 学习之旅_为什么聚焦是您数据科学之旅的关键
  2. 深入理解InnoDB(7)—系统表空间
  3. css背景图片添加url_CSS背景图片–如何向您的Div添加图片URL
  4. aws 静态网站_如何使用AWS托管静态网站-入门指南
  5. 将Javascript带到边缘设备
  6. 深入分析Spring 与 Spring MVC容器
  7. 二分检索函数lower_bound()和upper_bound()
  8. GCD牛逼的中枢调度器
  9. (转)mysql基础命令
  10. hdu1024Max Sum Plus Plus