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);
    }

描述

这是一个私人博客,记录平时的一些技术积累。

关于我

Email: 13073134757@163.com
GitHub: LinuxparaChen
© 主题借鉴Papyrus 原著作者:Hugo Sereno Ferreira. 原著主题.
Modify by LinuxparaChen.