Android 系统so文件路径修改

3,059 阅读3分钟

此处参考了https://blog.csdn.net/qq_32877045/article/details/52673246这篇文章,但是这篇文章的方法其实兼容性还是不全,本文在此之上做了补全。

在做动态加载so库的过程中,发现只使用全局加载so(System.load())其实并不是完美适用于所有情况,需要添加自定义so路径至系统so路径中才不会报错。

此外,纠正几个网上广为流传的几个错误认知:

1、只有System.load()才能加载非系统目录下的so文件,不能使用System.loadLibray()。

说法错误,其实这两个方法殊路同归,并没有什么本质区别,只是System.load()可以使用全局路径,我们把自定义路径加入到系统路径中,一样可以使用System.loadLibray()。

2、添加自定义so路径至系统so路径中这段代码需要写在Application中初始化。

说法错误,你啥时候用啥时候初始化不就完了。。懒加载就行了。。直接在你需要使用的类中添加static{}静态加载块中加载就行了。。感觉网上很多东西大家不思考就往上发,很多东西都是错的。。

还有一点经验:

1、不同的classLoader不可以加载同一个so文件,源码就是这么写的。。如果你想加载,那就复制一份,再去加载就ok了。

以下就是添加自定义so路径的代码了,欢迎拍砖。

 /**
     * android应用的so文件路径修改,添加自定义的so路径
     * @param context
     */
    public static void initNativeDirectory(Context context, String path) {
        if (hasDexClassLoader()) {
            try {
                createNewNativeDirAboveEqualApiLevel14(context, path);
            } catch (Exception e) {
                try {
                    createNewNativeDirAboveEqualApiLevel21(context, path);
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        } else {
            try {
                createNewNativeDirBelowApiLevel14(context, path);
            } catch (Exception e) {
                if (DEBUG) {
                    e.printStackTrace();
                }
            }
        }
    }

 /**
     * 4.0以上系统添加方案
     * @param context
     * @param path 自定义so路径
     * @throws Exception
     */
    private static void createNewNativeDirAboveEqualApiLevel14(Context context, String path) throws Exception {
        PathClassLoader pathClassLoader = (PathClassLoader) context.getClassLoader();
        Object pathList = getPathList(pathClassLoader);
        // 获取当前类的属性
        Object nativeLibraryDirectories = pathList.getClass().getDeclaredField("nativeLibraryDirectories");
        ((Field) nativeLibraryDirectories).setAccessible(true);
        // 获取 DEXPATHList中的属性值
        File[] files = (File[]) ((Field) nativeLibraryDirectories).get(pathList);
        Object newfiles = Array.newInstance(File.class, files.length + 1);
        // 添加自定义.so路径
        Array.set(newfiles, 0, new File(path));
        // 追加系统路径
        for (int i = 1; i < files.length + 1; i++) {
            Array.set(newfiles, i, files[i - 1]);
        }
        ((Field) nativeLibraryDirectories).set(pathList, newfiles);
    }

    /**
     * 4.0以下系统添加方案
     * @param context
     * @param path 自定义so路径
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    private static void createNewNativeDirBelowApiLevel14(Context context, String path)
            throws NoSuchFieldException, IllegalAccessException {
        PathClassLoader pathClassLoader = (PathClassLoader) context.getClassLoader();
        Field mLibPaths = pathClassLoader.getClass().getDeclaredField("mLibPaths");
        mLibPaths.setAccessible(true);
        String[] libs = (String[]) (mLibPaths).get(pathClassLoader);
        Object newPaths = Array.newInstance(String.class, libs.length + 1);
        // 添加自定义.so路径
        Array.set(newPaths, 0, path);
        // 追加系统路径
        for (int i = 1; i < libs.length + 1; i++) {
            Array.set(newPaths, i, libs[i - 1]);
        }
        mLibPaths.set(pathClassLoader, newPaths);
    }

    /**
     * 5.0以上系统添加方案
     * @param context
     * @param path 自定义so路径
     * @throws IllegalAccessException
     * @throws NoSuchFieldException
     * @throws ClassNotFoundException
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     */
    private static void createNewNativeDirAboveEqualApiLevel21(Context context, String path)
            throws IllegalAccessException, NoSuchFieldException, ClassNotFoundException,
            NoSuchMethodException, InvocationTargetException {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            return;
        }
        PathClassLoader pathClassLoader = (PathClassLoader) context.getClassLoader();
        Object pathList = getPathList(pathClassLoader);
        // 获取当前类的属性
        Object systemNativeLibraryDirectories = pathList.getClass()
                .getDeclaredField("systemNativeLibraryDirectories");
        ((Field) systemNativeLibraryDirectories).setAccessible(true);
        List<File> systemFiles = (List<File>) ((Field) systemNativeLibraryDirectories).get(pathList);
        systemFiles.add(new File(path));
        ((Field) systemNativeLibraryDirectories).set(pathList, systemFiles);

        Object nativeLibraryDirectories = pathList.getClass().getDeclaredField("nativeLibraryDirectories");
        ((Field) nativeLibraryDirectories).setAccessible(true);
        List<File> nativeFiles = (ArrayList<File>) ((Field) nativeLibraryDirectories).get(pathList);
        nativeFiles.add(new File(path));
        ((Field) nativeLibraryDirectories).set(pathList, nativeFiles);

        Class<?> elementClass = Class.forName("dalvik.system.DexPathList$Element");
        Constructor<?> element = null;
        element = elementClass.getConstructor(File.class, boolean.class, File.class, DexFile.class);
        Object nativeLibraryPathElements = pathList.getClass().getDeclaredField("nativeLibraryPathElements");
        ((Field) nativeLibraryPathElements).setAccessible(true);
        Object[] elementFiles = (Object[]) ((Field) nativeLibraryPathElements).get(pathList);
        Object newElementFiles = Array.newInstance(elementClass, elementFiles.length + 1);
        if (element != null) {
            try {
                Object newInstance = element.newInstance(
                        new File(path), true, null, null);
                Array.set(newElementFiles, 0, newInstance);
                for (int i = 1; i < elementFiles.length + 1; i++) {
                    Array.set(newElementFiles, i, elementFiles[i - 1]);
                }
                ((Field) nativeLibraryPathElements).set(pathList, newElementFiles);
            } catch (java.lang.IllegalArgumentException e) {
                Method elements = pathList.getClass().getDeclaredMethod("makePathElements", List.class);
                elements.setAccessible(true);
                Object invoke = elements.invoke(null, nativeFiles);

                Object nativeLibraryElements = pathList.getClass().getDeclaredField("nativeLibraryPathElements");
                ((Field) nativeLibraryElements).setAccessible(true);
                ((Field) nativeLibraryElements).set(pathList, invoke);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取 pathList
     * @param obj
     * @return
     * @throws ClassNotFoundException
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    private static Object getPathList(Object obj) throws ClassNotFoundException, NoSuchFieldException,
            IllegalAccessException {
        return getField(obj, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList");
    }

    /**
     * 通过反射获取成员变量
     * @param obj
     * @param cls
     * @param str
     * @return
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    private static Object getField(Object obj, Class cls, String str) throws NoSuchFieldException,
            IllegalAccessException {
        Field declaredField = cls.getDeclaredField(str);
        declaredField.setAccessible(true);
        return declaredField.get(obj);
    }

    /**
     *  仅对4.0以上做支持
     * @return
     */
    private static boolean hasDexClassLoader() {
        try {
            Class.forName("dalvik.system.BaseDexClassLoader");
            return true;
        } catch (ClassNotFoundException var1) {
            return false;
        }
    }