安卓手机如何打开 so文件 app安装包后缀名


前言

对于许多从事Android开发或Java工程的技术人员来说,使用JNI(Java Native Interface)进行开发应该是常见的操作。可能很少有人有机会深入了解JVM如何加载共享库(so文件)以及Android系统是如何处理这些库的。本文将通过分析代码和加载过程,带领大家了解JNI库加载的具体原理,帮助开发者理清相关概念。

System.load()与loadLibrary()

System.load()

System.load()是一个静态方法,它通过指定so文件的完整路径来加载该共享库。与之对应,Runtime.load()也是一种类似的加载方式。System.load()的路径参数必须包括完整的文件名和文件后缀。这个方法不仅仅是加载动态库,如果所给定的文件路径指向一个静态链接的原生库,JVM也会触发对应的JNI_OnLoad()函数,而不是加载动态库。

加载过程简述

在调用Runtime.load0()之前,首先会对目标so文件进行一系列检查。

load0()方先检查系统是否配置了SecurityManager。如果存在,会检查当前so文件的访问权限。如果权限不足,会抛出SecurityException并打印拒绝信息。如果路径为空,则抛出NullPointerException。

如果提供的so文件路径不是绝对路径,系统也会抛出UnsatisfiedLinkError,错误信息为“Expecting an absolute path of the library: xxx”。

如果这些检查都通过,load0()会继续调用ClassLoader.loadLibrary()来进一步加载该库。

System.loadLibrary()

System.loadLibrary()方法是另一种加载共享库的方式,不同于load(),它只需要传递库的名称,而无需指定完整的路径。在Android系统中,这个方自动查找特定目录(如/system/lib64/或应用的lib目录)并尝试加载拼接上lib前缀的共享库文件。

loadLibrary()要求传入的库名不包含任何平台特定的前缀、路径或扩展名。如果该库与JVM静态链接,则会调用与库名相关联的JNI_OnLoad方法。

加载过程简述

在调用Runtime.loadLibrary0()之前,系统会对目标so文件的路径进行检查。

和load()类似,loadLibrary0()会检查SecurityManager是否存在,并对so文件的访问权限进行验证。若验证不通过,抛出对应的异常。

loadLibrary()要求库名中不能含有路径分隔符(如/),否则会抛出UnsatisfiedLinkError,错误信息为“Directory separator should not appear in library name: xxx”。

如果一切检查通过,loadLibrary0()会继续加载该库。

ClassLoader.loadLibrary()

从调用栈中可以看到,不论是System.load()还是System.loadLibrary(),它们最终都会通过ClassLoader.loadLibrary()来实现共享库的加载。关键的区别在于传入的name参数是库的完整路径(load())还是仅仅是库的名称(loadLibrary()),同时是否指定了绝对路径。

通过getClassLoader()获取加载库所需的ClassLoader实例。

接着,系统会检查sys_paths(存储库路径的字符串数组)是否为空。如果为空,会调用initializePath("java.library.path")初始化用户自定义路径数组,然后调用initializePath("sun.boot.library.path")初始化系统路径数组。具体的初始化方式会在后文介绍。

接下来,系统会根据isAbsolute标志来决定是否直接加载共享库。如果name是绝对路径,系统将创建一个File对象,继续调用loadLibrary0()来加载该库。

如果name不是绝对路径,且ClassLoader存在,系统会通过findLibrary()方法根据库名查找其完整路径,随后创建指向该路径的File对象并加载该库。

如果findLibrary()返回空,系统会遍历所有系统路径,使用mapLibraryName()将库名转换为符合平台规范的完整库名,并加载该库。

查找路径过程

在检查了ClassLoader之后,若加载失败,系统会继续遍历sys_paths(系统路径数组),并尝试从不同的路径查找并加载目标库。

如果所有路径都查找无果,系统会将查找范围扩大到usr_paths,并重复相同的查找和加载过程。

如果依然找不到所需的库文件,最终会抛出UnsatisfiedLinkError,提示在java.library.path指定的路径下没有找到该库。

ClassLoader.initializePath()

initializePath()方法是系统用来初始化路径的一个关键步骤。它通过读取JVM的系统属性来获取所配置的路径,并将这些路径转化为一个数组。

getProperty()会从JVM中获取java.library.path属性的值,默认值为空字符串。

checkKey()方检查路径的合法性。如果路径值为空,抛出IllegalArgumentException;如果路径名为null,则抛出NullPointerException。

如果路径值有效,initializePath()会进一步处理这些路径,并根据需要进行安全性验证。

通过这些方法,JVM确保共享库能够按照正确的路径被加载到内存中。

本文详细解释了JVM如何通过System.load()和System.loadLibrary()加载原生库(so文件)的过程。无论是在Android还是传统Java环境中,理解这些方法背后的工作原理,都能帮助开发者更高效地调试和优化JNI相关的代码。