一步一步学习JNI
本文来自网易云社区
作者:孙有军
前言
本篇的主要目的就是JNI开发入门,使大家对JNI开发流程有一个大致的了解,后续再进行深入学习。
JNI不是Android特有的,JNI是Java Native Interface单词首字母的缩写,就是指用C或者C++开发的接口。JNI是JVM规范中的一部份,因此JNI程序在任何实现了JNI规范的Java虚拟机中都可以运行。
作为一个Android开发,这里不大书特书学习JNI的必要性和重要性,很多博客,文章都有讲述,这里主要给出入门步骤,让大家可以根据一步一步操作进行学习。
俗话说学习动最快的方式,就是找一个会的人,照着学,跟着做。照猫画虎,也能描出一个大概!
准备工作
我的开发环境为Mac os x 10.10.5,Eclipse 4.4.1(也可以不需要,有这个只是方便写代码,记事本,Sublime都可以)。由于环境为Mac,因此下面大部分的配置都是针对Mac的。
1,Java环境变量
由于需要Java环境,因此必须要设置Java环境变量,网上有很多方式教大家怎么设置。这里给出mac的设置方式。进入terminal。
1,输入cd ~进入当前用户
2,ls -al 可以看到一个.bash_profile文件,这就是设置环境变量的地方
3,如果没有输入:touch .bash_profile (用来修改文件时间戳,或者新建一个不存在的文件。)
4,输入 vim .bash_profile 输入E(表示进入编辑模式),输入I表示进入编辑输入
5,输入 export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home (这是我的目录,你可以去Library下查看自己的JavaVirtualMachines)
6,按ESC键退出输入模式,:wq 退出编辑模式
7,source .bash_profile (使配置生效)
8,echo $JAVA_HOME 如果有输入表示设置生效
实例操作
1,编写一个Java类
这里类名为Hello,包名为com.sunny(可以随便设置),代码如下:
- package com.sunny;public class Hello {
- static{
- //System.loadLibrary("Hello");
- System.load("/Users/doc/Jni/jniHello.jnilib");
- } public static native int getSum(int a, int b);
- public static void main(String[] args) { // 虚拟机扫描加载的lib路径
- System.out.println(System.getProperty("java.library.path"));
- int sum = getSum(2, 5);
- System.out.println(sum);
- }
- }
从上面的代码中我们设置了一个native函数,进行两数字的求和,并且在静态代码块中加载了动态链接库代码。这里有两种加载方式:
System.loadLibrary("Hello")
该方式不需要加入lib前缀,也不需要jnilib(不同的操作系统会有不同的后缀),虚拟机会自动从System.getProperty("java.library.path")路径进行扫描加载。后面会讲述怎么将自己的动态链接库加入到扫描路径。如果不设置会抛出UnsatisfiedLinkError异常。
System.load("/Users/doc/Jni/jniHello.jnilib");
该方式是设置的全路径,他会从设置的路径进加载。
2,生成.class文件
这里我们对上述的Java类编译生成 .class文件,我上述的代码在/Users/doc/Documents/Eclipse/Jni路径中,这里如果你直接采用Eclipse->Run As -> Java Application是不能运行的,因为这里还没有动态链接库,会抛出以下的异常。
- Exception in thread "main" java.lang.UnsatisfiedLinkError:
- Can't load library:
- /Users/doc/Jni/jniHello.jnilib
- at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1827)
- at java.lang.Runtime.load0(Runtime.java:809)
- at java.lang.System.load(System.java:1086)
- at com.sunny.Hello.<clinit>(Hello.java:7)
输入命令:
- javac src/com/sunny/Hello.java -d bin
-d 表示将生成的.class文件放到bin目录下,你可以去本地看看是否已经有该文件。
3,生成.h文件
输入命令:
- javah -jni -classpath bin -d jni com.sunny.Hello
上述生成的.h文件为comsunny_Hello.h,生成规则为包名加类名,.转为,尾缀为.h。
-jni为可选参数
-classpath 类查找路径,当前查找路径为bin目录
-d 与上述相同,表示将生成的.h放置到jni目录。
也可以通过-o参数生成指定的Hellox.h,该参数与-d互斥。命令如下:
- javah -jni -classpath bin -o Hellox.h com.sunny.Hello
生成的.h文件如下:
- /* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_sunny_Hello */#ifndef _Included_com_sunny_Hello#define _Included_com_sunny_Hello#ifdef __cplusplusextern "C" {#endif/*
- * Class: com_sunny_Hello
- * Method: getSum
- * Signature: (II)I
- */JNIEXPORT jint JNICALL Java_com_sunny_Hello_getSum
- (JNIEnv *, jclass, jint, jint);#ifdef __cplusplus}#endif#endif
4,编写.c文件实现.h
这里可以采用VS来进行编写,也可以用一个文本文档来编写。代码如下:
- #include "com_sunny_Hello.h"#ifdef __cplusplusextern "C" {#endif/*
- * Class: com_sunny_Hello
- * Method: getSum
- * Signature: (II)I
- */JNIEXPORT jint JNICALL Java_com_sunny_Hello_getSum
- (JNIEnv * env, jclass cls, jint a, jint b){ return a+b;
- }#ifdef __cplusplus}#endif
5,生成动态链接库
这里先补充前面的不同操作系统生成不同的名称。
Mac OS X : libHello.jnilib(又有前缀又有后缀)
Windows :Hello.dll(没有lib前缀)
Linux/Unix:libHello.so(是不是发现了android中常用的.so了)
Mac生成命令如下:
- gcc -dynamiclib -o /Users/doc/Jni/libHello.jnilib Hello.c -framework JavaVM -I/$JAVA_HOME/include -I/$JAVA_HOME/include/darwin
-dynamiclib表示生成动态链接库
-o表示生成动态链接库放置的位置于名字
-framework JavaVM -I:编译JNI需要用到JVM的头文件(jni.h),第一个目录是平台无关的,第二个目录是与操作系统平台相关的头文件,这里就是我们前面为什么要设置JAVA环境变量的原因。
Windows生成方式如下:
手头没有下载vs,因此从网上搜索了一下vs生成dll方式。开始菜单-->所有程序-->Microsoft Visual Studio 2012-->打开VS2012 X64本机工具命令提示,用cl命令编译成dll动态库:
- cl -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -LD Hello.c -FeHello.dll
-I :和mac一样,包含编译JNI必要的头文件
-LD:标识将指定的文件编译成动态链接库
-Fe:指定编译后生成的动态链接库的路径及文件名
Linux生成命令如下:
Linux生成,记住该生成方式,因为后续Android底层是linux系统,因此最终生成使用的都是.so动态链接,生成的命令如下:
- gcc -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -fPIC -shared Hello.c -o libHello.so
由于我本地下载的是mac版的jdk,编译会出现的错误,因为找不到对应linux平台的相关信息。
- In file included from Hello.c:1:
- In file included from ./com_sunny_Hello.h:2:
- //Library/Java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home/include/jni.h:45:10: fatal error: 'jni_md.h' file not found#include "jni_md.h"
- ^1 error generated.
这里我们为了演示怎么用命令行生成.so,因此把上述命令改成如下方式,后续会用AndroidStudio来生成.so动态链接库:
- gcc -I/$JAVA_HOME/include -I/$JAVA_HOME/include/darwin -fPIC -shared Hello.c -o libHello.so
-I 与-o与上述概念一致。
-fPIC: 编译成与位置无关的独立代码
-shared:同Fe与dynamiclib,编译成动态库
6,运行结果
这里可以采用Eclipse->Run As -> Java Application来运行了, 还要用命令行来运行。输入如下命令:
- java -classpath bin com.sunny.Hello
输出结果如下:
/Users/doc/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.
7
上述采用的是System.load("/Users/doc/Jni/jniHello.jnilib")方式,这里填入的是全路径,因此编译运行时,会从输入的路径去查找动态链接库,输出结果为:第一行是java.library.path扫描的路径,第二行7就是我们native返回的结果。
前面我们说了两种加载方式。上面编译的是全路径,不需要将当前的动态链接库放置到扫描路径下面。如果采用System.loadLibrary("Hello")方式,则需要将动态链接库放置到Java虚拟机能找到的地方。如果不进行设置则会抛出如下异常:
- Exception in thread "main" java.lang.UnsatisfiedLinkError: no Hello in java.library.path
- at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
- at java.lang.Runtime.loadLibrary0(Runtime.java:870)
- at java.lang.System.loadLibrary(System.java:1122)
- at com.sunny.Hello.<clinit>(Hello.java:6)
怎么链接到动态链接库呐?可以采用如下三种方式进行设置:
方法1:则需要将刚才生成的动态链接库放置到java.library.path扫描到的任何一个路径下面。
方法2:项目邮件选择properties
之后选择Java Build Path - Libraries - JRE System Library - Native library location - edit
之后选择本地生成的动态链接库
方法3:给jvm添加“-Djava.library.path=动态链接库搜索目录”参数,命令如下:
- java -Djava.library.path=/Users/doc/Jni/ -classpath bin
- com.sunny.Hello
设置了动态链接库后,方法二,与方法三输出结果如下:
/Users/doc/Jni/
7
从结果看出,这里输出的java.library.path路径与全路径输出已经不相同了,输出从默认查找的路径变成了设置的路径。
总结
这里主要做了一个初步的入门,后续再进行深入学习。如果有错误请指出。
网易云免费体验馆,0成本体验20+款云产品!
更多网易研发、产品、运营经验分享请访问网易云社区
相关文章:
【推荐】 分布式存储系统Kudu与HBase的简要分析与对比
一步一步学习JNI的更多相关文章
- 12.Linux软件安装 (一步一步学习大数据系列之 Linux)
1.如何上传安装包到服务器 有三种方式: 1.1使用图形化工具,如: filezilla 如何使用FileZilla上传和下载文件 1.2使用 sftp 工具: 在 windows下使用CRT 软件 ...
- (转) 一步一步学习ASP.NET 5 (四)- ASP.NET MVC 6四大特性
转发:微软MVP 卢建晖 的文章,希望对大家有帮助.原文:http://blog.csdn.net/kinfey/article/details/44459625 编者语 : 昨晚写好的文章居然csd ...
- (转) 一步一步学习ASP.NET 5 (二)- 通过命令行和sublime创建项目
转发:微软MVP 卢建晖 的文章,希望对大家有帮助. 注:昨天转发之后很多朋友指出了vNext的命名问题,原文作者已经做出了修改,后面的标题都适用 asp.net 5这个名称. 编者语 : 昨天发了第 ...
- 一步一步学习SignalR进行实时通信_1_简单介绍
一步一步学习SignalR进行实时通信\_1_简单介绍 SignalR 一步一步学习SignalR进行实时通信_1_简单介绍 前言 SignalR介绍 支持的平台 相关说明 OWIN 结束语 参考文献 ...
- 一步一步学习SignalR进行实时通信_8_案例2
原文:一步一步学习SignalR进行实时通信_8_案例2 一步一步学习SignalR进行实时通信\_8_案例2 SignalR 一步一步学习SignalR进行实时通信_8_案例2 前言 配置Hub 建 ...
- 一步一步学习SignalR进行实时通信_9_托管在非Web应用程序
原文:一步一步学习SignalR进行实时通信_9_托管在非Web应用程序 一步一步学习SignalR进行实时通信\_9_托管在非Web应用程序 一步一步学习SignalR进行实时通信_9_托管在非We ...
- 一步一步学习SignalR进行实时通信_7_非代理
原文:一步一步学习SignalR进行实时通信_7_非代理 一步一步学习SignalR进行实时通信\_7_非代理 SignalR 一步一步学习SignalR进行实时通信_7_非代理 前言 代理与非代理 ...
- 一步一步学习SignalR进行实时通信_5_Hub
原文:一步一步学习SignalR进行实时通信_5_Hub 一步一步学习SignalR进行实时通信\_5_Hub SignalR 一步一步学习SignalR进行实时通信_5_Hub 前言 Hub命名规则 ...
- 一步一步学习SignalR进行实时通信_6_案例
原文:一步一步学习SignalR进行实时通信_6_案例 一步一步学习SignalR进行实时通信\_6_案例1 一步一步学习SignalR进行实时通信_6_案例1 前言 类的定义 各块功能 后台 上线 ...
随机推荐
- Django logging的介绍
Django用的是Python buildin的logging模块. Python logging由四部分组成: Loggers - 记录器 Handles - 处理器 Filters - 过滤器 F ...
- springmvc小结(下)
1.@ModelAttribute 1.给共享的数据设置model数据设置,贴在形参上,也可以贴在方法上,设置一个model的key值 2.当controller方法返回一个对象的时候,,缺省值会把当 ...
- Kali-linux设置ProxyChains
ProxyChains是Linux和其他Unices下的代理工具.它可以使任何程序通过代理上网,允许TCP和DNS通过代理隧道,支持HTTP.SOCKS4和SOCKS5类型的代理服务器,并且可配置多个 ...
- 20145238-荆玉茗《网络对抗技术》-Web基础
20145238荆玉茗-<网络攻防>-Wbe基础 实践过程记录 实践过程记录 一.Apache 1.环境配置 使用apachectl start开启Apach,使用netstat -apt ...
- linux-资料汇集
1.http://www.debian.org/doc/ 2.鸟哥的私房菜 3.The Linux Command Line by William E. Shotts, Jr. 4.https://d ...
- 安装mysql的时候提示1045错误的解决方法
在安装mysql的时候提示1045错误,如图所示: 这种情况一般是之前卸载msyql的时候没有清理完一些文件之类的,导致给你提示存在安全问题,因此,只需要找到mysql一些系统的配置文件,并且将他们删 ...
- 关于JS的clone()函数编写的一些问题
问题讲述:用js 实现一个clone()克隆函数,该函数会把输入进去的不同类型值Number,String,Undefined,Boolean,Function,Null,Object,Array,R ...
- 一文读懂类加载机制--ClassLoader
一.什么是ClassLoader? 大家都知道,当我们写好一个Java程序之后,不是管是CS还是BS应用,都是由若干个.class文件组织而成的一个完整的Java应用程序,当程序在运行时,即会调用该程 ...
- 关于从Oracle数据库中删除表数据
1,删除表 drop 1.1 执行drop table table_name 语句 被 drop后的表被放在用户回收站(user_recyclebin)里,而没有被直接删除掉,回收站里的表可以被恢复 ...
- 微信小程序中的app文件介绍
[app] 一.app.json 1.对当前小程序的全局配置 2.页面路径.界面表现.网络超时时间.底部 tab 等 { "pages":[ "pages/index/i ...