美文网首页
Android ClassLoader原理(一)

Android ClassLoader原理(一)

作者: nothingren | 来源:发表于2017-12-08 10:43 被阅读0次

    Android的类加载机制遵循Java的双亲委派原理。其继承关系如下:

    ClassLoader <--- BaseDexClassLoader <---PathClassLoader、DexClassLoader
    

    PathClassLoader和DexClassLoader的源码如下:

    public class PathClassLoader extends BaseDexClassLoader {
    
        public PathClassLoader(String dexPath, ClassLoader parent) {
            super(dexPath, null, null, parent);
        }
    
        public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
            super(dexPath, null, librarySearchPath, parent);
        }
    }
    
    public class DexClassLoader extends BaseDexClassLoader {
        public DexClassLoader(String dexPath, String optimizedDirectory,
                String librarySearchPath, ClassLoader parent) {
            super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
        }
    }
    
    public class BaseDexClassLoader extends ClassLoader {
        private final DexPathList pathList;
    
        /**
         * Constructs an instance.
         *
         * @param dexPath the list of jar/apk files containing classes and
         * resources, delimited by {@code File.pathSeparator}, which
         * defaults to {@code ":"} on Android
         * @param optimizedDirectory directory where optimized dex files
         * should be written; may be {@code null}
         * @param librarySearchPath the list of directories containing native
         * libraries, delimited by {@code File.pathSeparator}; may be
         * {@code null}
         * @param parent the parent class loader
         */
        public BaseDexClassLoader(String dexPath, File optimizedDirectory,
                String librarySearchPath, ClassLoader parent) {
            super(parent);
            this.pathList = new DexPathList(this, dexPath, librarySearchPath, optimizedDirectory);
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
            Class c = pathList.findClass(name, suppressedExceptions);
            if (c == null) {
                ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);
                for (Throwable t : suppressedExceptions) {
                    cnfe.addSuppressed(t);
                }
                throw cnfe;
            }
            return c;
        }
    
      ...
    
      }
    

    PathClassLoader和DexClassLoader的区别仅仅在于构造方法中的optimizedDirectory是否为空,这里先不讨论optimizedDirectory的作用,后续会详细介绍。
    从BaseDexClassLoader的源码中可以看出来,加载一个类的时候,都是从pathList(DexPathList的实例)中查找的。DexPathList的findClass()方法源码如下:

    public Class findClass(String name, List<Throwable> suppressed) {
            //从dexElements数组中查找,dexElements就是用于存储dex文件信息,dexElements在BaseDexClassLoader的构造方法中创建。
            for (Element element : dexElements) {
                DexFile dex = element.dexFile;
    
                if (dex != null) {
                    Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
                    if (clazz != null) {
                        return clazz;
                    }
                }
            }
            if (dexElementsSuppressedExceptions != null) {
                suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
            }
            return null;
        }
    

    DexFile执行了dex文件的class查找工作,其loadClassBinaryName()方法源码如下:

       public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
           return defineClass(name, loader, mCookie, this, suppressed);
       }
    
       private static Class defineClass(String name, ClassLoader loader, Object cookie,
                                        DexFile dexFile, List<Throwable> suppressed) {
           Class result = null;
           try {
               result = defineClassNative(name, loader, cookie, dexFile);
           } catch (NoClassDefFoundError e) {
               if (suppressed != null) {
                   suppressed.add(e);
               }
           } catch (ClassNotFoundException e) {
               if (suppressed != null) {
                   suppressed.add(e);
               }
           }
           return result;
       }
    

    其中defineClassNative()方法是一个native方法后面会专门介绍。
    Android的ClassLoader加载机制可以总结为一句话:从遍历dexElements数组,挨着查找每个dex文件中是否有待加载打Class文件,如果有,则加载,如果没有,则接着查找下一个dex文件,直到将dexElements数组遍历完毕。很多插件化和热修复技术就是基于这个基本原则来设计。以热修复为例,将待修复的类打包成jar包,在应用启动的时候,将jar包解析成dex文件,并插入到dexElements数组的头部。这样等到待修复的类加载时,永远查找到的是dexElements头部第一个dex文件中的已经修复过的类。热修复的原理就这么简单,当然,热修复的实现方式还有很多,这里不一一说明。基于在dexElements数组头部插入的方式还有一个问题。那就是CLASS_ISPREVERIFIED标记。CLASS_ISPREVERIFIED的问题后面章节会详细说明。

    相关文章

      网友评论

          本文标题:Android ClassLoader原理(一)

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