Android类装载器DexClassLoader的简单使用
DexClassLoader 可以加载外部的 apk、jar 或 dex文件,
在java中,有个概念叫做“类加载器”(ClassLoader),它的作用就是动态的装载Class文件。
1. ClassLoader 的基础知识
ClassLoader主要对类的请求提供服务,当JVM需要某类时,它根据名称向ClassLoader要求这个类,然后由ClassLoader返回这个类的class对象。
无论是 JVM 还是 Dalvik 都是通过 ClassLoader 去加载所需要的类,而 ClassLoader 加载类的方式常称为双亲委托,
JVM 及 Dalvik 对类唯一的识别是 ClassLoader id + PackageName + ClassName,
2.Android平台的ClassLoader
Android中类加载器有BootClassLoader,URLClassLoader,
PathClassLoader,DexClassLoader,BaseDexClassLoader,等都最终继承自java.lang.ClassLoader。
1.java.lang.ClassLoader是所有ClassLoader的最终父类。
2.BootClassLoader
和java虚拟机中不同的是BootClassLoader是ClassLoader内部类,由java代码实现而不是c++实现,是Android平台上所有ClassLoader的最终parent,这个内部类是包内可见,所以我们没法使用。
3.URLClassLoader
只能用于加载jar文件,但是由于 dalvik 不能直接识别jar,所以在 Android 中无法使用这个加载器。
4.BaseDexClassLoader
PathClassLoader和DexClassLoader都继承自BaseDexClassLoader,其中的主要逻辑都是在BaseDexClassLoader完成的。这些源码在java/dalvik/system中。
BaseDexClassLoader的构造函数包含四个参数,分别为:
dexPath,指目标类所在的APK或jar文件的路径,类装载器将从该路径中寻找指定的目标类,该类必须是APK或jar的全路径.如果要包含多个路径,路径之间必须使用特定的分割符分隔,特定的分割符可以使用System.getProperty(“path.separtor”)获得。上面”支持加载APK、DEX和JAR,也可以从SD卡进行加载”指的就是这个路径,最终做的是将dexPath路径上的文件ODEX优化到内部位置optimizedDirectory,然后,再进行加载的。
File optimizedDirectory,由于dex文件被包含在APK或者Jar文件中,因此在装载目标类之前需要先从APK或Jar文件中解压出dex文件,该参数就是制定解压出的dex 文件存放的路径。这也是对apk中dex根据平台进行ODEX优化的过程。其实APK是一个程序压缩包,里面包含dex文件,ODEX优化就是把包里面的执行程序提取出来,就变成ODEX文件,因为你提取出来了,系统第一次启动的时候就不用去解压程序压缩包的程序,少了一个解压的过程。这样的话系统启动就加快了。为什么说是第一次呢?是因为DEX版本的也只有第一次会解压执行程序到 /data/dalvik-cache(针对PathClassLoader)或者optimizedDirectory(针对DexClassLoader)目录,之后也是直接读取目录下的的dex文件,所以第二次启动就和正常的差不多了。当然这只是简单的理解,实际生成的ODEX还有一定的优化作用。ClassLoader只能加载内部存储路径中的dex文件,所以这个路径必须为内部路径。
libPath,指目标类中所使用的C/C++库存放的路径
classload,是指该装载器的父装载器,一般为当前执行类的装载器,例如在Android中以context.getClassLoader()作为父装载器。
5.DexClassLoader
DexClassLoader支持加载APK、DEX和JAR,也可以从SD卡进行加载。
上面说dalvik不能直接识别jar,DexClassLoader却可以加载jar文件,这难道不矛盾吗?其实在BaseDexClassLoader里对”.jar”,”.zip”,”.apk”,”.dex”后缀的文件最后都会生成一个对应的dex文件,所以最终处理的还是dex文件,而URLClassLoader并没有做类似的处理。
一般我们都是用这个DexClassLoader来作为动态加载的加载器。
6.PathClassLoader
PathClassLoader没有将optimizedDirectory置为Null,也就是没设置优化后的存放路径。其实optimizedDirectory为null时的默认路径就是/data/dalvik-cache 目录。
PathClassLoader是用来加载Android系统类和应用的类,并且不建议开发者使用。
很多博客里说PathClassLoader只能加载已安装的apk的dex,其实这说的应该是在dalvik虚拟机上,在art虚拟机上PathClassLoader可以加载未安装的apk的dex(在art平台上已验证),然而在/data/dalvik-cache 确未找到相应的dex文件,怀疑是art虚拟机判断apk未安装,所以只是将apk优化后的odex放在内存中,之后进行释放,这只是个猜想,希望有知道的可以告知一下。因为dalvik上无法使用,所以我们也没法使用。
ClassLoder源码:
1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17/*
18 * Copyright (C) 2008 The Android Open Source Project
19 *
20 * Licensed under the Apache License, Version 2.0 (the "License");
21 * you may not use this file except in compliance with the License.
22 * You may obtain a copy of the License at
23 *
24 * http://www.apache.org/licenses/LICENSE-2.0
25 *
26 * Unless required by applicable law or agreed to in writing, software
27 * distributed under the License is distributed on an "AS IS" BASIS,
28 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29 * See the License for the specific language governing permissions and
30 * limitations under the License.
31 */
32
33package java.lang;
34
35import dalvik.system.PathClassLoader;
36import java.io.IOException;
37import java.io.InputStream;
38import java.net.URL;
39import java.nio.ByteBuffer;
40import java.security.ProtectionDomain;
41import java.util.Collection;
42import java.util.Collections;
43import java.util.Enumeration;
44import java.util.HashMap;
45import java.util.Map;
46import java.util.Set;
47
48/**
49 * Loads classes and resources from a repository. One or more class loaders are
50 * installed at runtime. These are consulted whenever the runtime system needs a
51 * specific class that is not yet available in-memory. Typically, class loaders
52 * are grouped into a tree where child class loaders delegate all requests to
53 * parent class loaders. Only if the parent class loader cannot satisfy the
54 * request, the child class loader itself tries to handle it.
55 * <p>
56 * {@code ClassLoader} is an abstract class that implements the common
57 * infrastructure required by all class loaders. Android provides several
58 * concrete implementations of the class, with
59 * {@link dalvik.system.PathClassLoader} being the one typically used. Other
60 * applications may implement subclasses of {@code ClassLoader} to provide
61 * special ways for loading classes.
62 * </p>
63 * @see Class
64 */
/*从一个仓库中加载资源文件或者类,许多类加载器通常在运行时被安装,子类加载器将所有请求委托给父类装载器,只有当父类装载器不能满足处理请求时,子装载器会尝试处理
65public abstract class ClassLoader {
66
67 /**
68 * The 'System' ClassLoader - the one that is responsible for loading
69 * classes from the classpath. It is not equal to the bootstrap class loader -
70 * that one handles the built-in classes.
71 *
72 * Because of a potential class initialization race between ClassLoader and
73 * java.lang.System, reproducible when using JDWP with "suspend=y", we defer
74 * creation of the system class loader until first use. We use a static
75 * inner class to get synchronization at init time without having to sync on
76 * every access.
77 *
78 * @see #getSystemClassLoader()
79 */
/*系统加载类,不同于启动加载类,它负责从类路径加载类,第一次使用时初始化
80 static private class SystemClassLoader {
81 public static ClassLoader loader = ClassLoader.createSystemClassLoader();
82 }
83
84 /**
85 * The parent ClassLoader.
86 */
87 private ClassLoader parent;
88
89 /**
90 * The packages known to the class loader.
91 */
92 private Map<String, Package> packages = new HashMap<String, Package>();
93
94 /**
95 * To avoid unloading individual classes, {@link java.lang.reflect.Proxy}
96 * only generates one class for each set of interfaces. This maps sets of
97 * interfaces to the proxy class that implements all of them. It is declared
98 * here so that these generated classes can be unloaded with their class
99 * loader.
100 *
101 * @hide
102 */
103 public final Map<Set<Class<?>>, Class<?>> proxyCache
104 = Collections.synchronizedMap(new HashMap<Set<Class<?>>, Class<?>>());
105
106 /**
107 * Create the system class loader. Note this is NOT the bootstrap class
108 * loader (which is managed by the VM). We use a null value for the parent
109 * to indicate that the bootstrap loader is our parent.
110 */
111 private static ClassLoader createSystemClassLoader() {
112 String classPath = System.getProperty("java.class.path", ".");
113
114 // String[] paths = classPath.split(":");
115 // URL[] urls = new URL[paths.length];
116 // for (int i = 0; i < paths.length; i++) {
117 // try {
118 // urls[i] = new URL("file://" + paths[i]);
119 // }
120 // catch (Exception ex) {
121 // ex.printStackTrace();
122 // }
123 // }
124 //
125 // return new java.net.URLClassLoader(urls, null);
126
127 // TODO Make this a java.net.URLClassLoader once we have those?
128 return new PathClassLoader(classPath, BootClassLoader.getInstance());
129 }
130
131 /**
132 * Returns the system class loader. This is the parent for new
133 * {@code ClassLoader} instances and is typically the class loader used to
134 * start the application.
135 */
136 public static ClassLoader getSystemClassLoader() {
137 return SystemClassLoader.loader;
138 }
139
140 /**
141 * Finds the URL of the resource with the specified name. The system class
142 * loader's resource lookup algorithm is used to find the resource.
143 *
144 * @return the {@code URL} object for the requested resource or {@code null}
145 * if the resource can not be found.
146 * @param resName
147 * the name of the resource to find.
148 * @see