【Android进阶】Android动态加载so文件
随着业务的增大,我们的业务代码也随之增多,包的大小是有增无减,所以适当的时候思考下:怎么做减法–减小包的体积。
结合最近在做的公司的项目,觉得动态加载so文件是一个很好精简apk包的方法。举个例子,视频播放器的SDK(如IJKplayer,VLC player),他们的各种视频的解码器一般都是通过C/C++编译的so文件,这些so文件其实都不小,这样导致我们从市场上下载的apk包很大,所以能不能让so文件不随apk一起发布呢,而是按需下载(只有当需要播放视频时才去服务器下载,然后再在本地load)。
为什么要动态加载
其实刚才已经解释了,可以有效避免apk安装包过大,因为这些so文件是依赖server的下发,本地只是load的过程。
其次,动态加载可以动态升级so文件,也是动态化的一部分。可以在不发版的情况下,升级so文件。
动态加载so文件,必须进行安全性校验,避免不必要的安全事故。
动态加载so文件
1. System.load(String filePath)
加载so文件分为动态加载和静态加载。
静态加载就是通过
System.loadLibrary(Sting libname);
来直接加载,对于一个app它只能加载system的和我们自己添加到jniLibs下的so文件。这个是我的demo项目的路径,静态加载回去这些路径下找到对应的库,否则抛出异常。
动态加载这是通过
System.load(String filePath)
来加载filePath对应路径下的so文件,这个路径不可以是外置SDcard等拓展路径,必须是/data/**{package}下。
所以下发的so没有权限放到图2-1下,只能通过加载的so文件路径的方式来动态加载so文件。
方案1: 将so文件copy到/data/**{package}下,system.load(filePath).
2. 支持静态加载
但是我们这样做还是解决不了问题,因为有些so文件加载的过程是放到sdkxia的,如百度地图sdk,已经封装了加载so文件(静态加载),即使你已经实现了方案1仍然扔出UnsatisfiedLinkError
的异常。
要弄清这个过程,就必须了解so的加载过程,以我的本地的android skd(Android)为例。
System源码
1 | public static void loadLibrary(String libname) { |
RunningTime
1 |
|
代码中的loader是ClassLoader
的对象,对于Android实际上是PathClassLoader
,这个意思就是当有classLoader时就通过PathClassLoader
的findLibrary(libraryName)
来加载(这个好像加载class),若无classLoader就通过mapLibraryName1()
建议大家看下native层怎么实现的:深入理解 System.loadLibrary
我们加载so看classLoader是怎么实现的,Android 5.0的源码源码:
BaseDexClassLoader.java的源码
1 |
|
pathList就是我们的DexPathList对象。
1 | final class DexPathList { |
看到了吧,会先找system下的so文件,再找nativeLibraryDirectories
下的,而这个nativeLibraryDirectories
就是我们的自己项目中jniLibs下对应的so文件的路径。
当以当我们静态加载时,其实找的so文件就是nativeLibraryDirectories
,所以我们可以以此作为突破口,利用反射,将这个nativeLibraryDirectories的开始处加上我们自己放so的文件夹下(感觉像QQ空间对class做patch的方式哦,其实替换旧的so文件这种可以可行的)。
开始hook啦。
1 | PathClassLoader pathClassLoader = (PathClassLoader) context.getApplicationContext().getClassLoader(); |
这个代码是在14-22都是ok的,但是23源码不是这样滴,看源码吧:
23的源码先放这,hook起来也不难。
Android 23源码建议hook
nativeLibraryPathElements
这个而不是nativeLibraryDirectories;
方案2:Hook DexPathList的nativeLibraryPathElements或者nativeLibraryDirectories,将我们自定义存so文件的文件夹作为他们的第一个元素。
出现的问题
刚开始我把所有视频相关的so文件扔到本地的一个文件下,再copy到/data/**{package}下,居然报32-bit instead of 64-bit
这个错误,我把so再放到jniLibs/armeabi下再跑可以啊,后来google了下发现有人在动态化时也遇到了,其中Anjon-github提到了一个方案:只要找任意一个32位的so文件(当然越小越好了)放到主程序中即可,于是我找了个1k的so文件放到了项目的jniLibs/armeabi下居然真的可以,这个原因不知为何,这个涉及到native代码,本人技术有限暂时没找到答案,不知道大家是否更好的解答或者解决方法。
- 本文链接:http://ownwell.github.io/2017/07/18/dynamic-load-so/
- 版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!