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 Class#getResource
149 */
150 public static URL getSystemResource(String resName) {
151 return SystemClassLoader.loader.getResource(resName);
152 }
153
154 /**
155 * Returns an enumeration of URLs for the resource with the specified name.
156 * The system class loader's resource lookup algorithm is used to find the
157 * resource.
158 *
159 * @return an enumeration of {@code URL} objects containing the requested
160 * resources.
161 * @param resName
162 * the name of the resource to find.
163 * @throws IOException
164 * if an I/O error occurs.
165 */
166 public static Enumeration<URL> getSystemResources(String resName) throws IOException {
167 return SystemClassLoader.loader.getResources(resName);
168 }
169
170 /**
171 * Returns a stream for the resource with the specified name. The system
172 * class loader's resource lookup algorithm is used to find the resource.
173 * Basically, the contents of the java.class.path are searched in order,
174 * looking for a path which matches the specified resource.
175 *
176 * @return a stream for the resource or {@code null}.
177 * @param resName
178 * the name of the resource to find.
179 * @see Class#getResourceAsStream
180 */
181 public static InputStream getSystemResourceAsStream(String resName) {
182 return SystemClassLoader.loader.getResourceAsStream(resName);
183 }
184
185 /**
186 * Constructs a new instance of this class with the system class loader as
187 * its parent.
188 */
189 protected ClassLoader() {
190 this(getSystemClassLoader(), false);
191 }
192
193 /**
194 * Constructs a new instance of this class with the specified class loader
195 * as its parent.
196 *
197 * @param parentLoader
198 * The {@code ClassLoader} to use as the new class loader's
199 * parent.
200 */
201 protected ClassLoader(ClassLoader parentLoader) {
202 this(parentLoader, false);
203 }
204
205 /*
206 * constructor for the BootClassLoader which needs parent to be null.
207 */
208 ClassLoader(ClassLoader parentLoader, boolean nullAllowed) {
209 if (parentLoader == null && !nullAllowed) {
210 throw new NullPointerException("parentLoader == null && !nullAllowed");
211 }
212 parent = parentLoader;
213 }
214
215 /**
216 * Constructs a new class from an array of bytes containing a class
217 * definition in class file format.
218 *
219 * @param classRep
220 * the memory image of a class file.
221 * @param offset
222 * the offset into {@code classRep}.
223 * @param length
224 * the length of the class file.
225 * @return the {@code Class} object created from the specified subset of
226 * data in {@code classRep}.
227 * @throws ClassFormatError
228 * if {@code classRep} does not contain a valid class.
229 * @throws IndexOutOfBoundsException
230 * if {@code offset < 0}, {@code length < 0} or if
231 * {@code offset + length} is greater than the length of
232 * {@code classRep}.
233 * @deprecated Use {@link #defineClass(String, byte[], int, int)}
234 */
235 @Deprecated
236 protected final Class<?> defineClass(byte[] classRep, int offset, int length)
237 throws ClassFormatError {
238 throw new UnsupportedOperationException("can't load this type of class file");
239 }
240
241 /**
242 * Constructs a new class from an array of bytes containing a class 从包含定义格式化类文件的bytes数组中构造一个新的类
243 * definition in class file format.
244 *
245 * @param className
246 * the expected name of the new class, may be {@code null} if not
247 * known.
248 * @param classRep
249 * the memory image of a class file.
250 * @param offset
251 * the offset into {@code classRep}.
252 * @param length
253 * the length of the class file.
254 * @return the {@code Class} object created from the specified subset of
255 * data in {@code classRep}.
256 * @throws ClassFormatError
257 * if {@code classRep} does not contain a valid class.
258 * @throws IndexOutOfBoundsException
259 * if {@code offset < 0}, {@code length < 0} or if
260 * {@code offset + length} is greater than the length of
261 * {@code classRep}.
262 */
263 protected final Class<?> defineClass(String className, byte[] classRep, int offset, int length)
264 throws ClassFormatError {
265 throw new UnsupportedOperationException("can't load this type of class file");
266 }
267
268 /**
269 * Constructs a new class from an array of bytes containing a class
270 * definition in class file format and assigns the specified protection
271 * domain to the new class. If the provided protection domain is
272 * {@code null} then a default protection domain is assigned to the class.
273 *
274 * @param className
275 * the expected name of the new class, may be {@code null} if not
276 * known.
277 * @param classRep
278 * the memory image of a class file.
279 * @param offset
280 * the offset into {@code classRep}.
281 * @param length
282 * the length of the class file.
283 * @param protectionDomain
284 * the protection domain to assign to the loaded class, may be
285 * {@code null}.
286 * @return the {@code Class} object created from the specified subset of
287 * data in {@code classRep}.
288 * @throws ClassFormatError
289 * if {@code classRep} does not contain a valid class.
290 * @throws IndexOutOfBoundsException
291 * if {@code offset < 0}, {@code length < 0} or if
292 * {@code offset + length} is greater than the length of
293 * {@code classRep}.
294 * @throws NoClassDefFoundError
295 * if {@code className} is not equal to the name of the class
296 * contained in {@code classRep}.
297 */
298 protected final Class<?> defineClass(String className, byte[] classRep, int offset, int length,
299 ProtectionDomain protectionDomain) throws java.lang.ClassFormatError {
300 throw new UnsupportedOperationException("can't load this type of class file");
301 }
302
303 /**
304 * Defines a new class with the specified name, byte code from the byte
305 * buffer and the optional protection domain. If the provided protection
306 * domain is {@code null} then a default protection domain is assigned to
307 * the class.
308 *
309 * @param name
310 * the expected name of the new class, may be {@code null} if not
311 * known.
312 * @param b
313 * the byte buffer containing the byte code of the new class.
314 * @param protectionDomain
315 * the protection domain to assign to the loaded class, may be
316 * {@code null}.
317 * @return the {@code Class} object created from the data in {@code b}.
318 * @throws ClassFormatError
319 * if {@code b} does not contain a valid class.
320 * @throws NoClassDefFoundError
321 * if {@code className} is not equal to the name of the class
322 * contained in {@code b}.
323 */
324 protected final Class<?> defineClass(String name, ByteBuffer b,
325 ProtectionDomain protectionDomain) throws ClassFormatError {
326
327 byte[] temp = new byte[b.remaining()];
328 b.get(temp);
329 return defineClass(name, temp, 0, temp.length, protectionDomain);
330 }
331
332 /**
333 * Overridden by subclasses, throws a {@code ClassNotFoundException} by 给子类覆盖的,当父加载器加载类失败时调用这个方法
334 * default. This method is called by {@code loadClass} after the parent
335 * {@code ClassLoader} has failed to find a loaded class of the same name.
336 *
337 * @param className
338 * the name of the class to look for.
339 * @return the {@code Class} object that is found.
340 * @throws ClassNotFoundException
341 * if the class cannot be found.
342 */
343 protected Class<?> findClass(String className) throws ClassNotFoundException {
344 throw new ClassNotFoundException(className);
345 }
346
347 /**
348 * Returns the class with the specified name if it has already been loaded
349 * by the VM or {@code null} if it has not yet been loaded.
350 *
351 * @param className
352 * the name of the class to look for.
353 * @return the {@code Class} object or {@code null} if the requested class
354 * has not been loaded.
355 */
356 protected final Class<?> findLoadedClass(String className) {
357 ClassLoader loader;
358 if (this == BootClassLoader.getInstance())
359 loader = null;
360 else
361 loader = this;
362 return VMClassLoader.findLoadedClass(loader, className);
363 }
364
365 /**
366 * Finds the class with the specified name, loading it using the system
367 * class loader if necessary.
368 *
369 * @param className
370 * the name of the class to look for.
371 * @return the {@code Class} object with the requested {@code className}.
372 * @throws ClassNotFoundException
373 * if the class can not be found.
374 */
375 protected final Class<?> findSystemClass(String className) throws ClassNotFoundException {
376 return Class.forName(className, false, getSystemClassLoader());
377 }
378
379 /**
380 * Returns this class loader's parent.
381 *
382 * @return this class loader's parent or {@code null}.
383 */
384 public final ClassLoader getParent() {
385 return parent;
386 }
387
388 /**
389 * Returns the URL of the resource with the specified name. This
390 * implementation first tries to use the parent class loader to find the
391 * resource; if this fails then {@link #findResource(String)} is called to
392 * find the requested resource.
393 *
394 * @param resName
395 * the name of the resource to find.
396 * @return the {@code URL} object for the requested resource or {@code null}
397 * if the resource can not be found
398 * @see Class#getResource
399 */
400 public URL getResource(String resName) {
401 URL resource = parent.getResource(resName);
402 if (resource == null) {
403 resource = findResource(resName);
404 }
405 return resource;
406 }
407
408 /**
409 * Returns an enumeration of URLs for the resource with the specified name.
410 * This implementation first uses this class loader's parent to find the
411 * resource, then it calls {@link #findResources(String)} to get additional //先用父装载器,再用findResources(resName)寻找额外的resources
412 * URLs. The returned enumeration contains the {@code URL} objects of both
413 * find operations.
414 *
415 * @return an enumeration of {@code URL} objects for the requested resource.
416 * @param resName
417 * the name of the resource to find.
418 * @throws IOException
419 * if an I/O error occurs.
420 */
421 @SuppressWarnings("unchecked")
422 public Enumeration<URL> getResources(String resName) throws IOException {
423
424 Enumeration<URL> first = parent.getResources(resName);
425 Enumeration<URL> second = findResources(resName);
426
427 return new TwoEnumerationsInOne(first, second);
428 }
429
430 /**
431 * Returns a stream for the resource with the specified name. See
432 * {@link #getResource(String)} for a description of the lookup algorithm
433 * used to find the resource.
434 *
435 * @return a stream for the resource or {@code null} if the resource can not be found
436 * @param resName
437 * the name of the resource to find.
438 * @see Class#getResourceAsStream
439 */
440 public InputStream getResourceAsStream(String resName) {
441 try {
442 URL url = getResource(resName);
443 if (url != null) {
444 return url.openStream();
445 }
446 } catch (IOException ex) {
447 // Don't want to see the exception.
448 }
449
450 return null;
451 }
452
453 /**
454 * Loads the class with the specified name. Invoking this method is
455 * equivalent to calling {@code loadClass(className, false)}. 调用这个方法相当于调用loadClass(className, false)
456 * <p>
457 * <strong>Note:</strong> In the Android reference implementation, the 在安卓的引用实现中第二个参数被忽视
458 * second parameter of {@link #loadClass(String, boolean)} is ignored
459 * anyway.
460 * </p>
461 *
462 * @return the {@code Class} object.
463 * @param className
464 * the name of the class to look for.
465 * @throws ClassNotFoundException
466 * if the class can not be found.
467 */
468 public Class<?> loadClass(String className) throws ClassNotFoundException {
469 return loadClass(className, false);
470 }
471
472 /**
473 * Loads the class with the specified name, optionally linking it after
474 * loading. The following steps are performed:
475 * <ol>
476 * <li> Call {@link #findLoadedClass(String)} to determine if the requested
477 * class has already been loaded.</li>
478 * <li>If the class has not yet been loaded: Invoke this method on the
479 * parent class loader.</li>
480 * <li>If the class has still not been loaded: Call
481 * {@link #findClass(String)} to find the class.</li>
482 * </ol>
483 * <p>
484 * <strong>Note:</strong> In the Android reference implementation, the
485 * {@code resolve} parameter is ignored; classes are never linked.
486 * </p>
487 *
488 * @return the {@code Class} object.
489 * @param className
490 * the name of the class to look for.
491 * @param resolve
492 * Indicates if the class should be resolved after loading. This 表明类是否在加载后被解决,被忽视此参数
493 * parameter is ignored on the Android reference implementation;
494 * classes are not resolved.
495 * @throws ClassNotFoundException
496 * if the class can not be found.
497 */
498 protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
499 Class<?> clazz = findLoadedClass(className);
500
501 if (clazz == null) {
502 ClassNotFoundException suppressed = null;
503 try {
504 clazz = parent.loadClass(className, false);
505 } catch (ClassNotFoundException e) {
506 suppressed = e;
507 }
508
509 if (clazz == null) {
510 try {
511 clazz = findClass(className);
512 } catch (ClassNotFoundException e) {
513 e.addSuppressed(suppressed);
514 throw e;
515 }
516 }
517 }
518
519 return clazz;
520 }
521
522 /**
523 * Forces a class to be linked (initialized). If the class has already been
524 * linked this operation has no effect.
525 * <p>
526 * <strong>Note:</strong> In the Android reference implementation, this
527 * method has no effect.
528 * </p>
529 *
530 * @param clazz
531 * the class to link.
532 */
533 protected final void resolveClass(Class<?> clazz) {
534 // no-op, doesn't make sense on android.
535 }
536
537 /**
538 * Finds the URL of the resource with the specified name. This
539 * implementation just returns {@code null}; it should be overridden in
540 * subclasses.
541 *
542 * @param resName
543 * the name of the resource to find.
544 * @return the {@code URL} object for the requested resource.
545 */
546 protected URL findResource(String resName) {
547 return null;
548 }
549
550 /**
551 * Finds an enumeration of URLs for the resource with the specified name.
552 * This implementation just returns an empty {@code Enumeration}; it should 查找enumeration of URLs for the resource,由子类覆盖
553 * be overridden in subclasses.
554 *
555 * @param resName
556 * the name of the resource to find.
557 * @return an enumeration of {@code URL} objects for the requested resource.
558 * @throws IOException
559 * if an I/O error occurs.
560 */
561 @SuppressWarnings( {
562 "unchecked", "unused"
563 })
564 protected Enumeration<URL> findResources(String resName) throws IOException {
565 return Collections.emptyEnumeration();
566 }
567
568 /**
569 * Returns the absolute path of the native library with the specified name, 返回指定名称的本地库,如果此方法返回null,虚拟机将在java.library.path寻找目录
570 * or {@code null}. If this method returns {@code null} then the virtual
571 * machine searches the directories specified by the system property
572 * "java.library.path".
573 * <p>
574 * This implementation always returns {@code null}.
575 * </p>
576 *
577 * @param libName
578 * the name of the library to find.
579 * @return the absolute path of the library.
580 */
581 protected String findLibrary(String libName) {
582 return null;
583 }
584
585 /**
586 * Returns the package with the specified name. Package information is
587 * searched in this class loader.
588 *
589 * @param name
590 * the name of the package to find.
591 * @return the package with the requested name; {@code null} if the package
592 * can not be found.
593 */
594 protected Package getPackage(String name) {
595 synchronized (packages) {
596 return packages.get(name);
597 }
598 }
599
600 /**
601 * Returns all the packages known to this class loader.
602 *
603 * @return an array with all packages known to this class loader.
604 */
605 protected Package[] getPackages() {
606 synchronized (packages) {
607 Collection<Package> col = packages.values();
608 Package[] result = new Package[col.size()];
609 col.toArray(result);
610 return result;
611 }
612 }
613
614 /**
615 * Defines and returns a new {@code Package} using the specified 使用指定信息定义和返回一个新的package
616 * information. If {@code sealBase} is {@code null}, the package is left
617 * unsealed. Otherwise, the package is sealed using this URL.
618 *
619 * @param name
620 * the name of the package.
621 * @param specTitle
622 * the title of the specification.
623 * @param specVersion
624 * the version of the specification.
625 * @param specVendor
626 * the vendor of the specification.
627 * @param implTitle
628 * the implementation title.
629 * @param implVersion
630 * the implementation version.
631 * @param implVendor
632 * the specification vendor.
633 * @param sealBase
634 * the URL used to seal this package or {@code null} to leave the
635 * package unsealed.
636 * @return the {@code Package} object that has been created.
637 * @throws IllegalArgumentException
638 * if a package with the specified name already exists.
639 */
640 protected Package definePackage(String name, String specTitle, String specVersion,
641 String specVendor, String implTitle, String implVersion, String implVendor, URL sealBase)
642 throws IllegalArgumentException {
643
644 synchronized (packages) {
645 if (packages.containsKey(name)) {
646 throw new IllegalArgumentException("Package " + name + " already defined");
647 }
648
649 Package newPackage = new Package(name, specTitle, specVersion, specVendor, implTitle,
650 implVersion, implVendor, sealBase);
651
652 packages.put(name, newPackage);
653
654 return newPackage;
655 }
656 }
657
658 /**
659 * Sets the signers of the specified class. This implementation does
660 * nothing.
661 *
662 * @param c
663 * the {@code Class} object for which to set the signers.
664 * @param signers
665 * the signers for {@code c}.
666 */
667 protected final void setSigners(Class<?> c, Object[] signers) {
668 }
669
670 /**
671 * Sets the assertion status of the class with the specified name.
672 * <p>
673 * <strong>Note: </strong>This method does nothing in the Android reference
674 * implementation.
675 * </p>
676 *
677 * @param cname
678 * the name of the class for which to set the assertion status.
679 * @param enable
680 * the new assertion status.
681 */
682 public void setClassAssertionStatus(String cname, boolean enable) {
683 }
684
685 /**
686 * Sets the assertion status of the package with the specified name.
687 * <p>
688 * <strong>Note: </strong>This method does nothing in the Android reference
689 * implementation.
690 * </p>
691 *
692 * @param pname
693 * the name of the package for which to set the assertion status.
694 * @param enable
695 * the new assertion status.
696 */
697 public void setPackageAssertionStatus(String pname, boolean enable) {
698 }
699
700 /**
701 * Sets the default assertion status for this class loader.
702 * <p>
703 * <strong>Note: </strong>This method does nothing in the Android reference
704 * implementation.
705 * </p>
706 *
707 * @param enable
708 * the new assertion status.
709 */
710 public void setDefaultAssertionStatus(boolean enable) {
711 }
712
713 /**
714 * Sets the default assertion status for this class loader to {@code false}
715 * and removes any package default and class assertion status settings.
716 * <p>
717 * <strong>Note:</strong> This method does nothing in the Android reference
718 * implementation.
719 * </p>
720 */
721 public void clearAssertionStatus() {
722 }
723}
724
725/*
726 * Provides a helper class that combines two existing URL enumerations into one.
727 * It is required for the getResources() methods. Items are fetched from the
728 * first enumeration until it's empty, then from the second one.
729 */
730class TwoEnumerationsInOne implements Enumeration<URL> {
731
732 private final Enumeration<URL> first;
733
734 private final Enumeration<URL> second;
735
736 public TwoEnumerationsInOne(Enumeration<URL> first, Enumeration<URL> second) {
737 this.first = first;
738 this.second = second;
739 }
740
741 @Override
742 public boolean hasMoreElements() {
743 return first.hasMoreElements() || second.hasMoreElements();
744 }
745
746 @Override
747 public URL nextElement() {
748 if (first.hasMoreElements()) {
749 return first.nextElement();
750 } else {
751 return second.nextElement();
752 }
753 }
754
755}
756
757/**
758 * Provides an explicit representation of the boot class loader. It sits at the
759 * head of the class loader chain and delegates requests to the VM's internal
760 * class loading mechanism.
761 */
762class BootClassLoader extends ClassLoader {
763
764 private static BootClassLoader instance;
765
766 @FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED")
767 public static synchronized BootClassLoader getInstance() {
768 if (instance == null) {
769 instance = new BootClassLoader();
770 }
771
772 return instance;
773 }
774
775 public BootClassLoader() {
776 super(null, true);
777 }
778
779 @Override
780 protected Class<?> findClass(String name) throws ClassNotFoundException {
781 return Class.classForName(name, false, null);
782 }
783
784 @Override
785 protected URL findResource(String name) {
786 return VMClassLoader.getResource(name);
787 }
788
789 @SuppressWarnings("unused")
790 @Override
791 protected Enumeration<URL> findResources(String resName) throws IOException {
792 return Collections.enumeration(VMClassLoader.getResources(resName));
793 }
794
795 /**
796 * Returns package information for the given package. Unfortunately, the
797 * Android BootClassLoader doesn't really have this information, and as a
798 * non-secure ClassLoader, it isn't even required to, according to the spec.
799 * Yet, we want to provide it, in order to make all those hopeful callers of
800 * {@code myClass.getPackage().getName()} happy. Thus we construct a Package
801 * object the first time it is being requested and fill most of the fields
802 * with dummy values. The Package object is then put into the ClassLoader's
803 * Package cache, so we see the same one next time. We don't create Package
804 * objects for null arguments or for the default package.
805 * <p>
806 * There a limited chance that we end up with multiple Package objects
807 * representing the same package: It can happen when when a package is
808 * scattered across different JAR files being loaded by different
809 * ClassLoaders. Rather unlikely, and given that this whole thing is more or
810 * less a workaround, probably not worth the effort.
811 */
812 @Override
813 protected Package getPackage(String name) {
814 if (name != null && !name.isEmpty()) {
815 synchronized (this) {
816 Package pack = super.getPackage(name);
817
818 if (pack == null) {
819 pack = definePackage(name, "Unknown", "0.0", "Unknown", "Unknown", "0.0",
820 "Unknown", null);
821 }
822
823 return pack;
824 }
825 }
826
827 return null;
828 }
829
830 @Override
831 public URL getResource(String resName) {
832 return findResource(resName);
833 }
834
835 @Override
836 protected Class<?> loadClass(String className, boolean resolve)
837 throws ClassNotFoundException {
838 Class<?> clazz = findLoadedClass(className);
839
840 if (clazz == null) {
841 clazz = findClass(className);
842 }
843
844 return clazz;
845 }
846
847 @Override
848 public Enumeration<URL> getResources(String resName) throws IOException {
849 return findResources(resName);
850 }
851}
852
853/**
854 * TODO Open issues - Missing / empty methods - Signer stuff - Protection
855 * domains - Assertions
856 */
重点看这部分逻辑:
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
499 Class<?> clazz = findLoadedClass(className); //先从已经加载的类中查找
500
501 if (clazz == null) {
502 ClassNotFoundException suppressed = null;
503 try {
504 clazz = parent.loadClass(className, false); //再从父加载器中查找
505 } catch (ClassNotFoundException e) {
506 suppressed = e;
507 }
508
509 if (clazz == null) {
510 try {
511 clazz = findClass(className); //子类的覆盖方法中查找
512 } catch (ClassNotFoundException e) {
513 e.addSuppressed(suppressed);
514 throw e;
515 }
516 }
517 }
518
519 return clazz;
}
520
521