JNI原理
0207-10-9
###JNI概述:
- JNI 全称是 Java Native Interface(Java 本地接口)单词首字母的缩写,
本地接口就是指用 C 和 C++ 开发的接口。 - JNI 是一个协议.
这个协议用来沟通java代码和外部的本地代码(c/c++).
通过这个协议,java代码就可以调用外部的c/c++代码,外部的c/c++代码也可以调用java代码.
###为什么要使用JNI?
- 复用已经存在的c代码, c语言有很多优秀的代码库.
- 效率上c/c++语言效率更高.
- java反编译非常容易.c语言反编译不容易.关键业务逻辑需要用c实现
在Java中有个native关键字来声明该方法为native方法,调用它最终将会调用native代码.
在调用该方法之前必须先调用System.loadLibrary来加载一个动态链接库,在Linux系统中动态链接库的格式一般为so,Windows系统中为dll.
如果我这样加载一个动态链接库System.loadLibrary(“native_lib”),那么系统会自动根据不同的平台拓展成真实的动态库文件名,Linux系统上会拓展成libnative_lib.so,而在Windows平台上则会拓展成native_lib.dll,在Android环境下,会查找以下文件/data/app/${packagename}/lib/${cpu平台}/libnative_lib.so,如果找不到这个文件便会报无法链接的错误.
如果这一步加载没问题,那我们调用以native声明的Java方法是如何寻找到native代码的呢?
我们首先分析System.loadLibrary方法干了什么。 我们可以看一下这个方法它的实现,即执行流程.
public static void loadLibrary(String libname) {
Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
}
代码实现很简单只是获取当前的类加载器(PathClassLoader)然后转交给了Runtime.loadLibrary0
synchronized void loadLibrary0(ClassLoader loader, String libname) {
//判断libname不能为目录(包含“/”)
if (libname.indexOf((int)File.separatorChar) != -1) {
throw new UnsatisfiedLinkError(
"Directory separator should not appear in library name: " + libname);
}
String libraryName = libname;
//两种情况,loader(类加载器)为空,和类加载器不为空。
if (loader != null) {
String filename = loader.findLibrary(libraryName);
if (filename == null) {
// It's not necessarily true that the ClassLoader used
// System.mapLibraryName, but the default setup does, and it's
// misleading to say we didn't find "libMyLibrary.so" when we
// actually searched for "liblibMyLibrary.so.so".
throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
System.mapLibraryName(libraryName) + "\"");
}
String error = doLoad(filename, loader);
if (error != null) {
throw new UnsatisfiedLinkError(error);
}
return;
}
String filename = System.mapLibraryName(libraryName);
List<String> candidates = new ArrayList<String>();
String lastError = null;
for (String directory : getLibPaths()) {
String candidate = directory + filename;
candidates.add(candidate);
if (IoUtils.canOpenReadOnly(candidate)) {
String error = doLoad(candidate, loader);
if (error == null) {
return; // We successfully loaded the library. Job done.
}
lastError = error;
}
}
if (lastError != null) {
throw new UnsatisfiedLinkError(lastError);
}
throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
}