SO库动态加载_directory separator should not appear in library n(2)

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

System.mapLibraryName("main); 通过so的名称拼接成文件路径返回的是libmain.so,nativeLibraryPathElements 是 native library 路径的集合, 它的是 DexPathList 初始化的时候赋值

 public String findNativeLibrary(String name) {
            maybeInit();

            if (zipDir == null) {
                String entryPath = new File(path, name).getPath();
                if (IoUtils.canOpenReadOnly(entryPath)) {
                    return entryPath;
                }
            } else if (urlHandler != null) {
                // Having a urlHandler means the element has a zip file.
                // In this case Android supports loading the library iff
                // it is stored in the zip uncompressed.
                String entryName = zipDir + '/' + name;
                if (urlHandler.isEntryStored(entryName)) {
                  return path.getPath() + zipSeparator + entryName;
                }
            }

            return null;
        }

我们看一下DexPathList的构造函数:

DexPathList(ClassLoader definingContext, String dexPath,
    String librarySearchPath, File optimizedDirectory, boolean isTrusted) {
  
    ...

    this.definingContext = definingContext;

        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
        // save dexPath for BaseDexClassLoader
        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
                                           suppressedExceptions, definingContext, isTrusted);

       
        this.nativeLibraryDirectories = splitPaths(librarySearchPath, false);
        this.systemNativeLibraryDirectories =
                splitPaths(System.getProperty("java.library.path"), true);
        List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
        allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);

        this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories);

        if (suppressedExceptions.size() > 0) {
            this.dexElementsSuppressedExceptions =
                suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
        } else {
            dexElementsSuppressedExceptions = null;
        }
    ...
}

在 DexPathList 的构造函数中,我们可以知道 nativeLibraryPathElements 是所有 Native Library 的集合。
DexPathList 是在 ActivityThread 中创建,ActivityThread 是在 App 启动时候创建的。关于 App 启动的启动流程,可以去找这方面的资料,自行查看。
总结一些 Native Library 的路径来源:
一个是 Native 库的原始路径 System.getProperty(“java.library.path****”), /system/lib/; /vendor/lib/; /product/lib/
另外一个是App启动时的 Lib 库路径

二、动态加载so库

根据上面Java层so加载流程我们可以知道,在System.loadLibrary(so名称)后把我们的so路径存储到了ClassLoader的pathList的nativeLibraryPathElements里面,我们知道咋热修复的知识里面是把解决bug的dex文件插入到PathList的Elements的前面,这样就可以保证我们ClassLoader findClass时会加载正确的class,那么我们得so是不是也可以这样做呢,把我们动态下载的so文件插入到nativeLibraryPathElements数组的前面。

1.将so文件复制到data目录下

 Log.d("Leon", getExternalCacheDir().getAbsolutePath());
        // 先调用 sdk 方法动态加载或者修复
        File mainSoPath = new File(Environment.getExternalStorageDirectory(),"so/libmain.so");

        File libSoPath = new File(getDir("lib",Context.MODE_PRIVATE),"so");
        if(!libSoPath.exists()){
            libSoPath.mkdirs();
        }
        File dst = new File(libSoPath,"libmain.so");
        try {
            FileUtil.copyFile(mainSoPath,dst);
        } catch (IOException e) {
            e.printStackTrace();
        }

 /**
     *
     * copy file
     *
     * @param src
     *            source file
     * @param dest
     *            target file
     * @throws IOException
     */
    public static void copyFile(File src, File dest) throws IOException {
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            if (!dest.exists()) {
                dest.createNewFile();
            }
            inChannel = new FileInputStream(src).getChannel();
            outChannel = new FileOutputStream(dest).getChannel();
            inChannel.transferTo(0, inChannel.size(), outChannel);
        } finally {
            if (inChannel != null) {
                inChannel.close();
            }
            if (outChannel != null) {
                outChannel.close();
            }
        }
    }

2.通过反射将我们的so文件插入到classLoader pathList的nativeLibraryPathElements前面

  ClassLoader classLoader = mContext.getClassLoader();

//        Field pathListField = ReflectUtil.getField(classLoader,"pathList");
//        pathListField.setAccessible(true);
        Object pathListObject = ReflectUtil.getFieldValue(classLoader, "pathList");

        Field nativeElementField = ReflectUtil.getField(pathListObject, "nativeLibraryPathElements");

        Object nativeLibraryElenentObject = nativeElementField.get(pathListObject);

        Class<?> elementClass = nativeLibraryElenentObject.getClass().getComponentType();
        Constructor<?> elementConstructor = elementClass.getConstructor(File.class);
        elementConstructor.setAccessible(true);
        Object firstElement = elementConstructor.newInstance(new File(absolutePath));


        Object newNativeElemenet = insertElementAtFirst(firstElement, nativeLibraryElenentObject);

        nativeElementField.set(pathListObject,newNativeElemenet);

 public static Object insertElementAtFirst(Object firstObj, Object array) {
        Class<?> localClass = array.getClass().getComponentType();
        int len = Array.getLength(array) + 1;
        Object result = Array.newInstance(localClass, len);
        Array.set(result, 0, firstObj);
        for (int k = 1; k < len; ++k) {
            Array.set(result, k, Array.get(array, k - 1));
        }
        return result;
    }


![img](https://img-blog.csdnimg.cn/img_convert/3867174e89f688d65bcc5f3121864efb.png)
![img](https://img-blog.csdnimg.cn/img_convert/72508092c23ee5d90ae01f51db67990d.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618636735)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618636735)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值