美文网首页EASY题
EP2-加载后的类存在的期限

EP2-加载后的类存在的期限

作者: DrunkPian0 | 来源:发表于2017-01-07 20:14 被阅读16次

    0x01 加载后的类存在的期限

    昨天说到的问题是,ClassLoader在一个App中至少有两个实例,一个是系统启动时创建的Boot类型的,一个是App中fork出来的;而且如果一个类被加载过,那么这个类永远不会被重新加载。 这个「永远」的期限是什么呢?
    我们可以看看ClassLoader的实现。ClassLoader是Abstract类型,我们要使用它的子类DexClassLoader、PathClassLoader 来实现类加载。
    从网络上可以查到,DexClassLoader、PathClassLoader 的区别是:

    • DexClassLoader 可以加载 jar/apk/dex,可以从 SD 卡中加载未安装的 apk;
    • PathClassLoader 只能加载系统中已经安装过的 apk;
    // DexClassLoader.java
    public class DexClassLoader extends BaseDexClassLoader {
        public DexClassLoader(String dexPath, String optimizedDirectory,
                String libraryPath, ClassLoader parent) {
            super(dexPath, new File(optimizedDirectory), libraryPath, parent);
        }
    }
    

    DexClassLoader的构造参数有dexPath,optimizedDirectory,libraryPath和parent。

    // PathClassLoader.java
    public class PathClassLoader extends BaseDexClassLoader {
        public PathClassLoader(String dexPath, ClassLoader parent) {
            super(dexPath, null, null, parent);
        }
        public PathClassLoader(String dexPath, String libraryPath,
                ClassLoader parent) {
            super(dexPath, null, libraryPath, parent);
        }
    }
    

    PathClassLoader的构造参数就只有dexPath和libraryPath,少了一个optimizedDirectory(super中传了null)。那么,看来这个optimizedDirectory就是为什么PathClassLoader「只能加载系统中已经安装过的 apk」的原因

    看看他们共同的父类BaseDexClassLoader里面的实现:

    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
                String libraryPath, ClassLoader parent) {
            super(parent);
            this.originalPath = dexPath;
            this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
        }
    

    创建了系统自动loadClass之后的dexPath,以及一个DexPathList对象。
    在DexPathList.java中有这样的方法:

    private static DexFile loadDexFile(File file, File optimizedDirectory)
            throws IOException {
        if (optimizedDirectory == null) {
            return new DexFile(file);
        } else {
            String optimizedPath = optimizedPathFor(file, optimizedDirectory);
            return DexFile.loadDex(file.getPath(), optimizedPath, 0);
        }
    }
    

    以及:

    /**
     * Converts a dex/jar file path and an output directory to an
     * output file path for an associated optimized dex file.
     * 为关联的最优化dexfile把dex/jar文件路径和输出目录转换成一个output文件路径
     */
    private static String optimizedPathFor(File path,
            File optimizedDirectory) {
        String fileName = path.getName();
        if (!fileName.endsWith(DEX_SUFFIX)) {
            int lastDot = fileName.lastIndexOf(".");
            if (lastDot < 0) {
                fileName += DEX_SUFFIX;
            } else {
                StringBuilder sb = new StringBuilder(lastDot + 4);
                sb.append(fileName, 0, lastDot);
                sb.append(DEX_SUFFIX);
                fileName = sb.toString();
            }
        }
        File result = new File(optimizedDirectory, fileName);
        return result.getPath();
    }
    

    也就是optimizedDirectory用来存储加载的dex文件,比如想要加载sd卡上的dex,就填写对应的文件路径。

    return DexFile.loadDex(file.getPath(), optimizedPath, 0);
    

    所以昨天的问题大概清楚了,既然是创建了一个文件来保存,而且这个文件是保存到应用内的(file.getPath()),所以类加载之后保存的「期限」就是在应用清空缓存或者卸载应用前

    前面我们了解到,凡是被父母加载过的类都不会重新被加载。这样的话,如果想要动态更新一个类,比如想要用到更新插件apk来实现「热修复」,就必须用一个不同的类,否则classloader会使用加载过的类。所以我们在使用新的插件的时候,要构造一个新的classLoader来加载这个插件的dex。或者,也许可以先清空之前加载过的dex的缓存路径。

    时间不够了,明天再说吧。

    -NOV22

    相关文章

      网友评论

        本文标题:EP2-加载后的类存在的期限

        本文链接:https://www.haomeiwen.com/subject/zzwfbttx.html