/ 前言 /
无论是 Android 开发者还是 JAVA 工程师应该都有使用过 JNI 开发,但对于 JVM 如何加载 so、Android 系统如何加载 so,可能鲜有时间了解 。
本文通过代码、流程解释,带大家快速了解其加载原理,扫清困惑 。
/ System#load() + loadLibrary() /
load()
System 提供的 load() 用于指定 so 的完整的路径名且带文件后缀并加载,等同于调用 Runtime 类提供的 load() 。
If the filename argument, when stripped of any platform-specific library prefix, path, and file extension, indicates a library whose name is, for example, L, and a native library called L is statically linked with the VM, then the JNI_OnLoad_L function exported by the library is invoked rather than attempting to load a dynamic library.
Eg.
System.load("/sdcard/path/libA.so")
步骤简述:
- 通过 Reflection 获取调用来源的 Class 实例
- 接着调用 Runtime 的 load0() 实现
load0() 首先获取系统的 SecurityManager 。当 SecurityManager 存在的话检查目标 so 文件的访问权限,权限不足的话打印拒绝信息、抛出 SecurityException ,如果 name 参数为空,抛出 NullPointerException 。如果 so 文件名非绝对路径的话,并不支持,并抛出 UnsatisfiedLinkError,message 为:
Expecting an absolute path of the library: xxx
针对 so 文件的权限检查和名称检查均通过的话,继续调用 ClassLoader 的 loadLibrary() 实现,需要留意的是绝对路径参数为 true 。
// java/lang/System.javapublic static void load(String filename) {Runtime.getRuntime().load0(Reflection.getCallerClass(), filename);}// java/lang/Runtime.javasynchronized void load0(Class<?> fromClass, String filename) {SecurityManager security = System.getSecurityManager();if (security != null) {security.checkLink(filename);}if (!(new File(filename).isAbsolute())) {throw new UnsatisfiedLinkError("Expecting an absolute path of the library: " + filename);}ClassLoader.loadLibrary(fromClass, filename, true);} loadLibrary()
System 类提供的 loadLibrary() 用于指定 so 的名称并加载,等同于调用 Runtime 类提供的 loadLibrary() 。在 Android 平台系统会自动去系统目录(/system/lib64/)、应用 lib 目录(/data/App/xxx/lib64/)下去找 libname 参数拼接了 lib 前缀的库文件 。
The libname argument must not contain any platform specific prefix, file extension or path.
If a native library called libname is statically linked with the VM, then the JNI_OnLoad_libname function exported by the library is invoked.
Eg.
System.loadLibrary("A") 步骤简述:
- 同样通过 Reflection 获取调用来源的 Class 实例
- 接着调用 Runtime 的 loadLibrary0() 实现
loadLibrary0() 首先获取系统的 SecurityManager,并检查目标 so 文件的访问权限 。权限不足或文件名为空的话和上面一样抛出 Exception 。确保 so 名称不包含 /,反之抛出 UnsatisfiedLinkError,message 为:
Directory separator should not appear in library name: xxx
检查通过后,同样调用 ClassLoader 的 loadLibrary() 实现继续下一步,只不过绝对路径参数为 false 。
// java/lang/System.javapublic static void loadLibrary(String libname) {Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);}// java/lang/Runtime.javasynchronized void loadLibrary0(Class<?> fromClass, String libname) {SecurityManager security = System.getSecurityManager();if (security != null) {security.checkLink(libname);}if (libname.indexOf((int)File.separatorChar) != -1) {throw new UnsatisfiedLinkError("Directory separator should not appear in library name: " + libname);}ClassLoader.loadLibrary(fromClass, libname, false);} / ClassLoader#loadLibrary() /
上面的调用栈可以看到无论是 load() 还是 loadLibrary() 最终都是调用 ClassLoader 的 loadLibrary(),主要区别在于 name 参数是 lib 完整路径、还是 lib 名称,以及是否是绝对路径参数 。
1.首先通过 getClassLoader() 获得加载源所属的 ClassLoader 实例
2.确保存放 libraries 路径的字符串数组 sys_paths 不为空 。尚且为空的话,调用 initializePath("java.library.path") 先初始化 usr 路径字符串数组,再调用 initializePath("sun.boot.library.path") 初始化 system 路径字符串数组 。initializePath() 具体见下章节 。
推荐阅读
- 一篇讲明白DevOps时代下的持续架构实践
- HTTP缓存看这一篇就够了
- |紫砂壶不挂水就不是好壶吗?
- 天津市|职场人的仕途,无非就是这三个阶段
- 2月逛花市,就选四种漂亮花,浇点水就能活,花开一整年
- 5种花一晒就蔫,放暗处越长越绿,养不好都是弄反了
- 市场营销专业就业方向怎么样,最新市场营销专业当前的就业形势分析
- |老婆打疫苗非要穿露肩连衣裙,戴着口罩就不怕丢脸了吗?
- 出口营销策划方案怎么做,未来出口营销就业前景
- 贺兰山|自己一知半解,却嘲讽壮壮没文化。那就说说《满江红》的贺兰山吧
