Android随笔-ClassLoader

概述

任何一个Java程序都有一个或多个class,程序运行时,需要将class文件加载到JVM中才可以使用,负责加载class文件的就是ClassLoader。每一个Class对象内部都有一个classLoader字段来标记该对象应该由哪一个ClassLoader加载。

public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
    ...
    /** defining class loader, or null for the "bootstrap" system loader. */
    private transient ClassLoader classLoader;

代码中的ClassLoader就是本文要概述的类加载器。

分类

ClassLoader是抽象类,Android使用最多的ClassLoader实现类有三种:BootClassLoader,PathClassLoader,DexClassLoader。

public abstract class ClassLoader {

    static private class SystemClassLoader {
        public static ClassLoader loader = ClassLoader.createSystemClassLoader();
    }

  • BootClassLoader
    private static class BootClassLoader extends BuiltinClassLoader {
        BootClassLoader(URLClassPath bcp) {
            super((String)null, (BuiltinClassLoader)null, bcp);
        }

        protected Class<?> loadClassOrNull(String cn) {
            return ClassLoaders.JLA.findBootstrapClassOrNull(this, cn);
        }
    }

加载Android Framework中的class文件。
测试:

 Log.d(TAG, "Activity的ClassLoader:" + Activity.class.getClassLoader().toString());

结果:

ActivityClassLoaderjava.lang.BootClassLoader@93afdaf
  • PathClassLoader
public class PathClassLoader extends BaseDexClassLoader {
    public PathClassLoader(String dexPath, ClassLoader parent) {
        super((String)null, (File)null, (String)null, (ClassLoader)null);
        throw new RuntimeException("Stub!");
    }

    public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
        super((String)null, (File)null, (String)null, (ClassLoader)null);
        throw new RuntimeException("Stub!");
    }
}

加载Android应用程序类,可以指定dex,以及jar、zip、apk中的classes.dex文件。
测试:

      Log.d(TAG, "AppCompatActivity的ClassLoader:" + AppCompatActivity.class.getClassLoader().toString());

结果:

AppCompatActivityClassLoaderdalvik.system.PathClassLoader[DexPathList[

说明AppCompatActivity并不属于Framework。

  • DexClassLoader
public class DexClassLoader extends BaseDexClassLoader {
    public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
        super((String)null, (File)null, (String)null, (ClassLoader)null);
        throw new RuntimeException("Stub!");
    }
}

optimizedDirectory:dexopt的优化文件odex的产出目录,为null时的默认路径为:/data/dalvik-cache。
DexClassLoader加载指定的dex,以及jar、zip、apk中的classes.dex文件,多用于热修复和插件化。

联系

PathClassLoader和DexClassLoader都继承自BaseDexClassLoader。两者的唯一区别是DexClassLoader多个一个optimizedDirectory参数,并且会将其创建的File对象传递给super。两者都可以加载指定dex,以及jar、zip、apk中的classes.dex文件。
在这里插入图片描述

流程

在这里插入图片描述

  1. 创建DexPathList 对象pathList,同时将dex文件加载到内存中,生成DexFile对象,再生成Element,并添加到Element数组dexElements。
  public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String librarySearchPath, ClassLoader parent, boolean isTrusted) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);

        if (reporter != null) {
            reportClassLoaderChain();
        }
    }
  1. BaseDexClassLoader调用findClass方法。
 @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;
    }

  1. 调用DexPathList 的findClass()方法。
public Class<?> findClass(String name, List<Throwable> suppressed) {
        for (Element element : dexElements) {
            Class<?> clazz = element.findClass(name, definingContext, suppressed);
            if (clazz != null) {
                return clazz;
            }
        }

        if (dexElementsSuppressedExceptions != null) {
            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
        }
        return null;
    }
    public Class<?> findClass(String name, ClassLoader definingContext,
                List<Throwable> suppressed) {
            return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
                    : null;
        }

遍历dexElements里面的元素,通过遍历所有加载过的dex文件,调用loadClassBinaryName方法寻找想要的类,如果有就直接返回。

双亲委托机制

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
            // 首先,检查class是否已被加载过
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                try {
                    if (parent != null) {
                    	// 如果父类已加载,则使用父类进行加载
                        c = parent.loadClass(name, false);
                    } else {
                        // 如果父类为null,则使用findBootstrapClassOrNull进行加载
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                }
                if (c == null) {
                    // 如果没有发现,就自己findclass
                    c = findClass(name);
                }
            }
            return c;
    }

如果父类为null时,其实使用findBootstrapClassOrNull加载其实相当于什么也没做。

    private Class<?> findBootstrapClassOrNull(String name)
    {
        return null;
    }

类加载器在加载类时候,会采用双亲委托机制。首先将加载任务委托给父类进行加载,若有多级父类,则一次递归,若父类可以完成加载,则成功返回;如果父类没有办法完成加载任务就自己加载。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值