JNI简介
JAVA NATIVE INTERFACE 顾名思义,就是JAVA语言调用其他NATIVE语言的一个接口。
这样说来可能有点晦涩,说白了,JNI就是JAVA想调用其他语言的桥梁--比如C,C++(这种就叫做NATIVE语言)等。
下面盗个图来解释一下关系:
友情链接:https://blog.csdn.net/itachi85/article/details/73459880
---------------------------------------------------------------------------------------------------------------
下面用bsdiff增量更新来演示jni的调用过程
--准备工作:
1.bzip2(这个很关键,后面会用到)下载 http://www.bzip.org/downloads.html(后面主要会用到这个),以及bsdiff源码 (用于了解)
2.编译环境,建议最好在linux环境下,用GCC编译(博主在windows下面搞了几天没搞明白,有大神可以尝试)
相关项目实例:https://github.com/ccj659/incremental-update-master
IDEA平台下JNI编程:https://blog.csdn.net/huachao1001/article/details/53906237
-- 开始编码(以下均在linux环境下进行,以JDK1.8为例):
1.JAVA中创建NATIVE方法(其中的add方法主要用于main方法的测试,如果不需要可以忽略)
package test;
public class TestBsdiff {
public native static int diff(String oldFile, String newFile, String patchch);
public native static int add(int a, int b);
static {
System.loadLibrary("app_bsdiff");
System.out.println("loading C libary ");
}
public static void main(String args[]) {
int sum = 0;
sum = TestBsdiff.add(1, 2);
System.out.print(sum);
}
}
这里静态块用于加载C代码(NATIVE代码)编程生成的文件--适用于windows为*.dll,适用于linux为*.so,我们这里以linux下的*.so为例
编写好了JAVA代码,编译成C的头文件 *.h:
jdk1.8以上编译使用 javac -h . TestBsdiff.java
jdk1.8以下编译使用 javac TestBsdiff.java
javah TestBsdiff
编译后,就会在当前目录生成test_TestBsdiff.h 即C语言的头文件 (不难看出,这个test是java文件的包名,下划线后面是类名)然后,将test_TestBsdiff.h放到src目录下
2.将下载好的bzip2解压,将其中的bzlib.h,bzlib_private.h,bzlib.c,blocksort.c,bsdiff.c,bzip.c,compress.c,crctable.c,decompress.c,huffman.c,randtable.c解压到src目录下(与test_TestBsdiff.h位置一样)
然后要修改bsdiff.c如下,在头中加入#include<jni.h> ,#include<test_TestBsdiff.h>(引入头文件并实现其中的方法)
#include <jni.h>
#include <test_TestBsdiff.h>
#include <sys/types.h>
修改其中的main函数为bsdiff_main
int bsdiff_main(int argc,char *argv[])
最后在末尾增加代码段,实现头文件中的两个方法test_TestBsdiff_diff与Java_test_TestBsdiff_add
JNIEXPORT jint JNICALL Java_test_TestBsdiff_diff
(JNIEnv *env, jclass cls, jstring old, jstring new, jstring patch){
int argc=4;
char * argv[argc];
argv[0]="bsdiff";
argv[1]=(char*)((*env)->GetStringUTFChars(env,old, 0));
argv[2]=(char*)((*env)->GetStringUTFChars(env,new, 0));
argv[3]=(char*)((*env)->GetStringUTFChars(env,patch, 0));
int ret=bsdiff_main(argc, argv);
(*env)->ReleaseStringUTFChars(env,old,argv[1]);
(*env)->ReleaseStringUTFChars(env,new,argv[2]);
(*env)->ReleaseStringUTFChars(env,patch,argv[3]);
return ret;
}
JNIEXPORT jint JNICALL Java_test_TestBsdiff_add
(JNIEnv *env, jobject obj, jint a, jint b) {
int var = 0;
var = a + b;
return var;
}
这个时候,进入到src目录下,执行执行如下,将这些源码编译成*.so文件
gcc -fPIC -I/usr/local/jdk1.8.0_91/include/ -I/usr/local/jdk1.8.0_91/include/linux/ -I/home/hotfixas2/workspace/src -shared -o libapp_bsdiff.so bzlib.c bzip2.c blocksort.c compress.c crctable.c decompress.c huffman.c randtable.c bsdiff.c
以上指令略长,稍微说明一下
(1)首先解释一下 gcc指令
-fPIC的含义表示编译成位置独立的代码(Position Independent Code),具体可以自己百度一下
-shared的含义就是指定生成动态链接库*.so
-o 表示输出的文件类型,这里面是*.so
(2)解释一下其中的一些参数
-I/usr/local/jdk1.8.0_91/include/ 意思就是要通过扫描使用jdk1.8包这里面的文件,jni.h
-I/usr/local/jdk1.8.0_91/include/linux/ 同上,也是要用到其中的文件,jni_md.h
libapp_bsdiff.so 这个要特别注意一下,虽然在java里面System.loadLibrary(“app_bsdiff.so”),但是在linux里面必须要加上lib这个前缀,表示这个是动态链接库
这个时候,如果文件的位置都正确的话,就会在src目录下生成一个叫做libapp_bsdiff.so的动态链接库文件!!
3.现在有了java的方法声明,C语言的实现app_bsdiff.so,那么就差最后这一环节----就是告诉JAVA程序,去哪里找实现类app_bsdiff.so。最后这一步也是尤为重要的一步,否则会报java.lang.unsatisfiedLinkError
设置linux的LD_LIBRARY_PATH变量:
临时导入:$ export LD_LIBRARY_PATH=/home/hotfixas2/workspace/src(也就是app_bsdiff.so所在的目录)
永久导入:可以在~/.bashrc或者~./bash_profile中末尾加入export语句
export LD_LIBRARY_PATH=/home/hotfixas2/workspace/src(也就是app_bsdiff.so所在的目录)
修改完毕后,记得关掉当前终端的并重新打开一个新的终端使之生效
4.自测是否已经链接成功
进入到src目录下,TestBsdiff.class所在目录src/test, 执行指令java -cp . test.TestBsdiff,如果运行成功
将会输出 loading C library 3
5.既然链接成功,那么就可以试试diff方法是否可以正常解析(找两个apk的新旧两个包,放到指定目录下)
将apk包放入指定的目录,也就是java中diff的前两个参数,最后一个参数是比对成功后生成patch的名字
执行一下,看看能否生成想要的patch
TestBsdiff.diff("old.apk","new.apk","result.patch");
---------------------------------------------
英文较好的同学,请参考 : jni API文档英文链接