ClassLoader

ClassLoader


bootstrap classloader(启动类加载器)
>>这个ClassLoader在JVM运行的时候加载java核心的API以满足java程序最基本的需求,其中就包括用户定义的ClassLoader


通过java程序实现的ClassLoader:
>>ExtClassLoader : 这个ClassLoader是用来加载java的扩展API的,也就是/lib/ext中的类.
>>AppClassLoader : 这个 ClassLoader是用来加载用户机器上CLASSPATH设置目录中的Class的,通常在没有指定ClassLoader的情况下,程序员自定义的类就由该ClassLoader进行加载.


程序运行: 

当运行一个程序的时候,JVM启动,运行bootstrap classloader,该ClassLoader加载java核心API(ExtClassLoader和AppClassLoader也在此时被加载),然后调用ExtClassLoader加载扩展API,最后AppClassLoader加载CLASSPATH目录下定义的Class,这就是一个程序最基本的加载流程。


上面大概讲解了一下ClassLoader的作用以及一个最基本的加载流程,接下来将讲解一下ClassLoader加载的方式,这里就不得不讲一下ClassLoader在这里使用了双亲委托模式进行类加载。

每一个自定义ClassLoader都必须继承ClassLoader这个抽象类,而每个ClassLoader都会有一个parent ClassLoader,我们可以看一下ClassLoader这个抽象类中有一个getParent()方法,这个方法用来返回当前 ClassLoader的parent,注意,这个parent不是指的被继承的类,而是在实例化该ClassLoader时指定的一个 ClassLoader,如果这个parent为null,那么就默认该ClassLoader的parent是bootstrap classloader,这个parent有什么用呢?

我们可以考虑这样一种情况,假设我们自定义了一个 ClientDefClassLoader,我们使用这个自定义的ClassLoader加载java.lang.String,那么这里String是否会被这个ClassLoader加载呢?事实上java.lang.String这个类并不是被这个ClientDefClassLoader加载,而是由bootstrap classloader进行加载,为什么会这样?实际上这就是双亲委托模式的原因,因为在任何一个自定义ClassLoader加载一个类之前,它都会先委托它的父亲ClassLoader进行加载,只有当父亲ClassLoader无法加载成功后,才会由自己加载,在上面这个例子里,因为 java.lang.String是属于java核心API的一个类,所以当使用ClientDefClassLoader加载它的时候,该 ClassLoader会先委托它的父亲ClassLoader进行加载,上面讲过,当ClassLoader的parent为null 时,ClassLoader的parent就是bootstrap classloader,所以在ClassLoader的最顶层就是bootstrap classloader,因此最终委托到bootstrap classloader的时候,bootstrap classloader就会返回String的Class。


为什么要使用这种双亲委托模式呢?

第一个原因就是因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。

第二个原因就是考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时被加载,所以用户自定义类是无法加载一个自定义的 ClassLoader。

ClassLoader.java

[code="java"]

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package java.lang;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.ByteBuffer;
import java.security.ProtectionDomain;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Map;
import java.util.HashMap;

import dalvik.system.PathClassLoader;
import dalvik.system.VMStack;

/**
* Loads classes and resources from a repository. One or more class loaders are
* installed at runtime. These are consulted whenever the runtime system needs a
* specific class that is not yet available in-memory. Typically, class loaders
* are grouped into a tree where child class loaders delegate all requests to
* parent class loaders. Only if the parent class loader cannot satisfy the
* request, the child class loader itself tries to handle it.
* <p>
* {@code ClassLoader} is an abstract class that implements the common
* infrastructure required by all class loaders. Android provides several
* concrete implementations of the class, with
* {@link dalvik.system.PathClassLoader} being the one typically used. Other
* applications may implement subclasses of {@code ClassLoader} to provide
* special ways for loading classes.
* </p>
*
* @since Android 1.0
* @see Class
*/
public abstract class ClassLoader {

// BEGIN android-note
/*
* Because of a potential class initialization race between ClassLoader and
* java.lang.System, reproducible when using JDWP with "suspend=y", we defer
* creation of the system class loader until first use. We use a static
* inner class to get synchronization at init time without having to sync on
* every access.
*/
// END android-note
/**
* The 'System' ClassLoader - the one that is responsible for loading
* classes from the classpath. It is not equal to the bootstrap class loader -
* that one handles the built-in classes.
*
* @see #getSystemClassLoader()
*/
static private class SystemClassLoader {
public static ClassLoader loader = ClassLoader.createSystemClassLoader();
}

/**
* The parent ClassLoader.
*/
private ClassLoader parent;

/**
* The packages known to the class loader.
*/
private Map<String, Package> packages = new HashMap<String, Package>();

/**
* Create the system class loader. Note this is NOT the bootstrap class
* loader (which is managed by the VM). We use a null value for the parent
* to indicate that the bootstrap loader is our parent.
*/
private static ClassLoader createSystemClassLoader() {
String classPath = System.getProperty("java.class.path", ".");

// String[] paths = classPath.split(":");
// URL[] urls = new URL[paths.length];
// for (int i = 0; i < paths.length; i++) {
// try {
// urls[i] = new URL("file://" + paths[i]);
// }
// catch (Exception ex) {
// ex.printStackTrace();
// }
// }
//
// return new java.net.URLClassLoader(urls, null);

// TODO Make this a java.net.URLClassLoader once we have those?
return new PathClassLoader(classPath, BootClassLoader.getInstance());
}

/**
* Returns the system class loader. This is the parent for new
* {@code ClassLoader} instances and is typically the class loader used to
* start the application. If a security manager is present and the caller's
* class loader is neither {@code null} nor the same as or an ancestor of
* the system class loader, then this method calls the security manager's
* checkPermission method with a RuntimePermission("getClassLoader")
* permission to ensure that it is ok to access the system class loader. If
* not, a {@code SecurityException} is thrown.
*
* @return the system class loader.
* @throws SecurityException
* if a security manager exists and it does not allow access to
* the system class loader.
* @since Android 1.0
*/
public static ClassLoader getSystemClassLoader() {
SecurityManager smgr = System.getSecurityManager();
if (smgr != null) {
ClassLoader caller = VMStack.getCallingClassLoader();
if (caller != null && !caller.isAncestorOf(SystemClassLoader.loader)) {
smgr.checkPermission(new RuntimePermission("getClassLoader"));
}
}

return SystemClassLoader.loader;
}

/**
* Finds the URL of the resource with the specified name. The system class
* loader's resource lookup algorithm is used to find the resource.
*
* @return the {@code URL} object for the requested resource or {@code null}
* if the resource can not be found.
* @param resName
* the name of the resource to find.
* @see Class#getResource
* @since Android 1.0
*/
public static URL getSystemResource(String resName) {
return SystemClassLoader.loader.getResource(resName);
}

/**
* Returns an enumeration of URLs for the resource with the specified name.
* The system class loader's resource lookup algorithm is used to find the
* resource.
*
* @return an enumeration of {@code URL} objects containing the requested
* resources.
* @param resName
* the name of the resource to find.
* @throws IOException
* if an I/O error occurs.
* @since Android 1.0
*/
public static Enumeration<URL> getSystemResources(String resName) throws IOException {
return SystemClassLoader.loader.getResources(resName);
}

/**
* Returns a stream for the resource with the specified name. The system
* class loader's resource lookup algorithm is used to find the resource.
* Basically, the contents of the java.class.path are searched in order,
* looking for a path which matches the specified resource.
*
* @return a stream for the resource or {@code null}.
* @param resName
* the name of the resource to find.
* @see Class#getResourceAsStream
* @since Android 1.0
*/
public static InputStream getSystemResourceAsStream(String resName) {
return SystemClassLoader.loader.getResourceAsStream(resName);
}

/**
* Constructs a new instance of this class with the system class loader as
* its parent.
*
* @throws SecurityException
* if a security manager exists and it does not allow the
* creation of a new {@code ClassLoader}.
* @since Android 1.0
*/
protected ClassLoader() {
this(getSystemClassLoader(), false);
}

/**
* Constructs a new instance of this class with the specified class loader
* as its parent.
*
* @param parentLoader
* The {@code ClassLoader} to use as the new class loader's
* parent.
* @throws SecurityException
* if a security manager exists and it does not allow the
* creation of new a new {@code ClassLoader}.
* @since Android 1.0
*/
protected ClassLoader(ClassLoader parentLoader) {
this(parentLoader, false);
}

/*
* constructor for the BootClassLoader which needs parent to be null.
*/
ClassLoader(ClassLoader parentLoader, boolean nullAllowed) {
SecurityManager smgr = System.getSecurityManager();
if (smgr != null) {
smgr.checkCreateClassLoader();
}

if (parentLoader == null && !nullAllowed) {
throw new NullPointerException(
"Parent ClassLoader may not be null");
}

parent = parentLoader;
}

/**
* Constructs a new class from an array of bytes containing a class
* definition in class file format.
*
* @param classRep
* the memory image of a class file.
* @param offset
* the offset into {@code classRep}.
* @param length
* the length of the class file.
* @return the {@code Class} object created from the specified subset of
* data in {@code classRep}.
* @throws ClassFormatError
* if {@code classRep} does not contain a valid class.
* @throws IndexOutOfBoundsException
* if {@code offset < 0}, {@code length < 0} or if
* {@code offset + length} is greater than the length of
* {@code classRep}.
* @deprecated Use {@link #defineClass(String, byte[], int, int)}
* @since Android 1.0
*/
@Deprecated
protected final Class<?> defineClass(byte[] classRep, int offset, int length)
throws ClassFormatError {

return VMClassLoader.defineClass(this, classRep, offset, length, null);
}

/**
* Constructs a new class from an array of bytes containing a class
* definition in class file format.
*
* @param className
* the expected name of the new class, may be {@code null} if not
* known.
* @param classRep
* the memory image of a class file.
* @param offset
* the offset into {@code classRep}.
* @param length
* the length of the class file.
* @return the {@code Class} object created from the specified subset of
* data in {@code classRep}.
* @throws ClassFormatError
* if {@code classRep} does not contain a valid class.
* @throws IndexOutOfBoundsException
* if {@code offset < 0}, {@code length < 0} or if
* {@code offset + length} is greater than the length of
* {@code classRep}.
* @since Android 1.0
*/
protected final Class<?> defineClass(String className, byte[] classRep, int offset, int length)
throws ClassFormatError {

// TODO Define a default ProtectionDomain on first use
return defineClass(className, classRep, offset, length, null);
}

/**
* Constructs a new class from an array of bytes containing a class
* definition in class file format and assigns the specified protection
* domain to the new class. If the provided protection domain is
* {@code null} then a default protection domain is assigned to the class.
*
* @param className
* the expected name of the new class, may be {@code null} if not
* known.
* @param classRep
* the memory image of a class file.
* @param offset
* the offset into {@code classRep}.
* @param length
* the length of the class file.
* @param protectionDomain
* the protection domain to assign to the loaded class, may be
* {@code null}.
* @return the {@code Class} object created from the specified subset of
* data in {@code classRep}.
* @throws ClassFormatError
* if {@code classRep} does not contain a valid class.
* @throws IndexOutOfBoundsException
* if {@code offset < 0}, {@code length < 0} or if
* {@code offset + length} is greater than the length of
* {@code classRep}.
* @throws NoClassDefFoundError
* if {@code className} is not equal to the name of the class
* contained in {@code classRep}.
* @since Android 1.0
*/
protected final Class<?> defineClass(String className, byte[] classRep, int offset, int length,
ProtectionDomain protectionDomain) throws java.lang.ClassFormatError {

return VMClassLoader.defineClass(this, className, classRep, offset, length,
protectionDomain);
}

/**
* Defines a new class with the specified name, byte code from the byte
* buffer and the optional protection domain. If the provided protection
* domain is {@code null} then a default protection domain is assigned to
* the class.
*
* @param name
* the expected name of the new class, may be {@code null} if not
* known.
* @param b
* the byte buffer containing the byte code of the new class.
* @param protectionDomain
* the protection domain to assign to the loaded class, may be
* {@code null}.
* @return the {@code Class} object created from the data in {@code b}.
* @throws ClassFormatError
* if {@code b} does not contain a valid class.
* @throws NoClassDefFoundError
* if {@code className} is not equal to the name of the class
* contained in {@code b}.
* @since Android 1.0
*/
protected final Class<?> defineClass(String name, ByteBuffer b,
ProtectionDomain protectionDomain) throws ClassFormatError {

byte[] temp = new byte[b.remaining()];
b.get(temp);
return defineClass(name, temp, 0, temp.length, protectionDomain);
}

/**
* Overridden by subclasses, throws a {@code ClassNotFoundException} by
* default. This method is called by {@code loadClass} after the parent
* {@code ClassLoader} has failed to find a loaded class of the same name.
*
* @param className
* the name of the class to look for.
* @return the {@code Class} object that is found.
* @throws ClassNotFoundException
* if the class cannot be found.
* @since Android 1.0
*/
protected Class<?> findClass(String className) throws ClassNotFoundException {
throw new ClassNotFoundException(className);
}

/**
* Returns the class with the specified name if it has already been loaded
* by the virtual machine or {@code null} if it has not yet been loaded.
*
* @param className
* the name of the class to look for.
* @return the {@code Class} object or {@code null} if the requested class
* has not been loaded.
* @since Android 1.0
*/
protected final Class<?> findLoadedClass(String className) {
ClassLoader loader;
if (this == BootClassLoader.getInstance())
loader = null;
else
loader = this;
return VMClassLoader.findLoadedClass(loader, className);
}

/**
* Finds the class with the specified name, loading it using the system
* class loader if necessary.
*
* @param className
* the name of the class to look for.
* @return the {@code Class} object with the requested {@code className}.
* @throws ClassNotFoundException
* if the class can not be found.
* @since Android 1.0
*/
protected final Class<?> findSystemClass(String className) throws ClassNotFoundException {
return Class.forName(className, false, getSystemClassLoader());
}

/**
* Returns this class loader's parent.
*
* @return this class loader's parent or {@code null}.
* @throws SecurityException
* if a security manager exists and it does not allow to
* retrieve the parent class loader.
* @since Android 1.0
*/
public final ClassLoader getParent() {
SecurityManager smgr = System.getSecurityManager();
if (smgr != null) {
smgr.checkPermission(new RuntimePermission("getClassLoader"));
}

return parent;
}

/**
* Returns the URL of the resource with the specified name. This
* implementation first tries to use the parent class loader to find the
* resource; if this fails then {@link #findResource(String)} is called to
* find the requested resource.
*
* @param resName
* the name of the resource to find.
* @return the {@code URL} object for the requested resource or {@code null}
* if either the resource can not be found or a security manager
* does not allow to access the resource.
* @see Class#getResource
* @since Android 1.0
*/
public URL getResource(String resName) {
URL resource = null;

resource = parent.getResource(resName);

if (resource == null) {
resource = findResource(resName);
}

return resource;
}

/**
* Returns an enumeration of URLs for the resource with the specified name.
* This implementation first uses this class loader's parent to find the
* resource, then it calls {@link #findResources(String)} to get additional
* URLs. The returned enumeration contains the {@code URL} objects of both
* find operations.
*
* @return an enumeration of {@code URL} objects for the requested resource.
* @param resName
* the name of the resource to find.
* @throws IOException
* if an I/O error occurs.
* @since Android 1.0
*/
@SuppressWarnings("unchecked")
public Enumeration<URL> getResources(String resName) throws IOException {

Enumeration first = parent.getResources(resName);
Enumeration second = findResources(resName);

return new TwoEnumerationsInOne(first, second);
}

/**
* Returns a stream for the resource with the specified name. See
* {@link #getResource(String)} for a description of the lookup algorithm
* used to find the resource.
*
* @return a stream for the resource or {@code null} if either the resource
* can not be found or a security manager does not allow to access
* the resource.
* @param resName
* the name of the resource to find.
* @see Class#getResourceAsStream
* @since Android 1.0
*/
public InputStream getResourceAsStream(String resName) {
try {
URL url = getResource(resName);
if (url != null) {
return url.openStream();
}
} catch (IOException ex) {
// Don't want to see the exception.
}

return null;
}

/**
* Loads the class with the specified name. Invoking this method is
* equivalent to calling {@code loadClass(className, false)}.
* <p>
* <strong>Note:</strong> In the Android reference implementation, the
* second parameter of {@link #loadClass(String, boolean)} is ignored
* anyway.
* </p>
*
* @return the {@code Class} object.
* @param className
* the name of the class to look for.
* @throws ClassNotFoundException
* if the class can not be found.
* @since Android 1.0
*/
public Class<?> loadClass(String className) throws ClassNotFoundException {
return loadClass(className, false);
}

/**
* Loads the class with the specified name, optionally linking it after
* loading. The following steps are performed:
* <ol>
* <li> Call {@link #findLoadedClass(String)} to determine if the requested
* class has already been loaded.</li>
* <li>If the class has not yet been loaded: Invoke this method on the
* parent class loader.</li>
* <li>If the class has still not been loaded: Call
* {@link #findClass(String)} to find the class.</li>
* </ol>
* <p>
* <strong>Note:</strong> In the Android reference implementation, the
* {@code resolve} parameter is ignored; classes are never linked.
* </p>
*
* @return the {@code Class} object.
* @param className
* the name of the class to look for.
* @param resolve
* Indicates if the class should be resolved after loading. This
* parameter is ignored on the Android reference implementation;
* classes are not resolved.
* @throws ClassNotFoundException
* if the class can not be found.
* @since Android 1.0
*/
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
Class<?> clazz = findLoadedClass(className);

if (clazz == null) {
try {
clazz = parent.loadClass(className, false);
} catch (ClassNotFoundException e) {
// Don't want to see this.
}

if (clazz == null) {
clazz = findClass(className);
}
}

return clazz;
}

/**
* Forces a class to be linked (initialized). If the class has already been
* linked this operation has no effect.
* <p>
* <strong>Note:</strong> In the Android reference implementation, this
* method has no effect.
* </p>
*
* @param clazz
* the class to link.
* @since Android 1.0
*/
protected final void resolveClass(Class<?> clazz) {
// no-op, doesn't make sense on android.
}

/**
* Indicates whether this class loader is the system class loader. This
* method must be provided by the virtual machine vendor, as it is used by
* other provided class implementations in this package. A sample
* implementation of this method is provided by the reference
* implementation. This method is used by
* SecurityManager.classLoaderDepth(), currentClassLoader() and
* currentLoadedClass(). Returns true if the receiver is a system class
* loader.
* <p>
* Note that this method has package visibility only. It is defined here to
* avoid the security manager check in getSystemClassLoader, which would be
* required to implement this method anywhere else.
* </p>
*
* @return {@code true} if the receiver is a system class loader
* @see Class#getClassLoaderImpl()
*/
final boolean isSystemClassLoader() {
return false;
}

/**
* <p>
* Returns true if the receiver is ancestor of another class loader. It also
* returns true if the two class loader are equal.
* </p>
* <p>
* Note that this method has package visibility only. It is defined here to
* avoid the security manager check in getParent, which would be required to
* implement this method anywhere else. The method is also required in other
* places where class loaders are accesses.
* </p>
*
* @param child
* A child candidate
* @return {@code true} if the receiver is ancestor of, or equal to,
* the parameter
*/
final boolean isAncestorOf(ClassLoader child) {
for (ClassLoader current = child; current != null;
current = current.parent) {
if (current == this) {
return true;
}
}
return false;
}

/**
* Finds the URL of the resource with the specified name. This
* implementation just returns {@code null}; it should be overridden in
* subclasses.
*
* @param resName
* the name of the resource to find.
* @return the {@code URL} object for the requested resource.
* @since Android 1.0
*/
protected URL findResource(String resName) {
return null;
}

/**
* Finds an enumeration of URLs for the resource with the specified name.
* This implementation just returns an empty {@code Enumeration}; it should
* be overridden in subclasses.
*
* @param resName
* the name of the resource to find.
* @return an enumeration of {@code URL} objects for the requested resource.
* @throws IOException
* if an I/O error occurs.
* @since Android 1.0
*/
@SuppressWarnings( {
"unchecked", "unused"
})
protected Enumeration<URL> findResources(String resName) throws IOException {
return EmptyEnumeration.getInstance();
}

/**
* Returns the absolute path of the native library with the specified name,
* or {@code null}. If this method returns {@code null} then the virtual
* machine searches the directories specified by the system property
* "java.library.path".
* <p>
* This implementation always returns {@code null}.
* </p>
*
* @param libName
* the name of the library to find.
* @return the absolute path of the library.
* @since Android 1.0
*/
protected String findLibrary(String libName) {
return null;
}

/**
* Returns the package with the specified name. Package information is
* searched in this class loader.
*
* @param name
* the name of the package to find.
* @return the package with the requested name; {@code null} if the package
* can not be found.
* @since Android 1.0
*/
protected Package getPackage(String name) {
synchronized (packages) {
Package p = packages.get(name);
return p;
}
}

/**
* Gets the package with the specified name, searching it in the specified
* class loader.
*
* @param loader
* the class loader to search the package in.
* @param name
* the name of the package to find.
* @return the package with the requested name; {@code null} if the package
* can not be found.
* @since Android 1.0
*/
static Package getPackage(ClassLoader loader, String name) {
return loader.getPackage(name);
}

/**
* Returns all the packages known to this class loader.
*
* @return an array with all packages known to this class loader.
* @since Android 1.0
*/
protected Package[] getPackages() {
synchronized (packages) {
Collection<Package> col = packages.values();
Package[] result = new Package[col.size()];
col.toArray(result);
return result;
}
}

/**
* Defines and returns a new {@code Package} using the specified
* information. If {@code sealBase} is {@code null}, the package is left
* unsealed. Otherwise, the package is sealed using this URL.
*
* @param name
* the name of the package.
* @param specTitle
* the title of the specification.
* @param specVersion
* the version of the specification.
* @param specVendor
* the vendor of the specification.
* @param implTitle
* the implementation title.
* @param implVersion
* the implementation version.
* @param implVendor
* the specification vendor.
* @param sealBase
* the URL used to seal this package or {@code null} to leave the
* package unsealed.
* @return the {@code Package} object that has been created.
* @throws IllegalArgumentException
* if a package with the specified name already exists.
* @since Android 1.0
*/
protected Package definePackage(String name, String specTitle, String specVersion,
String specVendor, String implTitle, String implVersion, String implVendor, URL sealBase)
throws IllegalArgumentException {

synchronized (packages) {
if (packages.containsKey(name)) {
throw new IllegalArgumentException("Package " + name + " already defined");
}

Package newPackage = new Package(name, specTitle, specVersion, specVendor, implTitle,
implVersion, implVendor, sealBase);

packages.put(name, newPackage);

return newPackage;
}
}

/**
* Gets the signers of the specified class. This implementation returns
* {@code null}.
*
* @param c
* the {@code Class} object for which to get the signers.
* @return signers the signers of {@code c}.
* @since Android 1.0
*/
final Object[] getSigners(Class<?> c) {
return null;
}

/**
* Sets the signers of the specified class. This implementation does
* nothing.
*
* @param c
* the {@code Class} object for which to set the signers.
* @param signers
* the signers for {@code c}.
* @since Android 1.0
*/
protected final void setSigners(Class<?> c, Object[] signers) {
return;
}

/**
* <p>
* This must be provided by the VM vendor. It is used by
* SecurityManager.checkMemberAccess() with depth = 3. Note that
* checkMemberAccess() assumes the following stack when called:<br>
* </p>
*
* <pre>
* < user code &gt; <- want this class
* Class.getDeclared*();
* Class.checkMemberAccess();
* SecurityManager.checkMemberAccess(); <- current frame
* </pre>
*
* <p>
* Returns the ClassLoader of the method (including natives) at the
* specified depth on the stack of the calling thread. Frames representing
* the VM implementation of java.lang.reflect are not included in the list.
* </p>
* Notes:
* <ul>
* <li>This method operates on the defining classes of methods on stack.
* NOT the classes of receivers.</li>
* <li>The item at depth zero is the caller of this method</li>
* </ul>
*
* @param depth
* the stack depth of the requested ClassLoader
* @return the ClassLoader at the specified depth
*/
static final ClassLoader getStackClassLoader(int depth) {
Class<?>[] stack = VMStack.getClasses(depth + 1, false);
if(stack.length < depth + 1) {
return null;
}
return stack[depth].getClassLoader();
}

/**
* This method must be provided by the VM vendor, as it is called by
* java.lang.System.loadLibrary(). System.loadLibrary() cannot call
* Runtime.loadLibrary() because this method loads the library using the
* ClassLoader of the calling method. Loads and links the library specified
* by the argument.
*
* @param libName
* the name of the library to load
* @param loader
* the classloader in which to load the library
* @throws UnsatisfiedLinkError
* if the library could not be loaded
* @throws SecurityException
* if the library was not allowed to be loaded
* <p>
* <strong>Note: </strong>This method does nothing in the Android reference
* implementation.
* </p>
*/
static void loadLibraryWithClassLoader(String libName, ClassLoader loader) {
return;
}

/**
* This method must be provided by the VM vendor, as it is called by
* java.lang.System.load(). System.load() cannot call Runtime.load() because
* the library is loaded using the ClassLoader of the calling method. Loads
* and links the library specified by the argument. No security check is
* done.
* <p>
* <strong>Note: </strong>This method does nothing in the Android reference
* implementation.
* </p>
*
* @param libName
* the name of the library to load
* @param loader
* the classloader in which to load the library
* @param libraryPath
* the library path to search, or null
* @throws UnsatisfiedLinkError
* if the library could not be loaded
*/
static void loadLibraryWithPath(String libName, ClassLoader loader, String libraryPath) {
return;
}

/**
* Sets the assertion status of the class with the specified name.
* <p>
* <strong>Note: </strong>This method does nothing in the Android reference
* implementation.
* </p>
*
* @param cname
* the name of the class for which to set the assertion status.
* @param enable
* the new assertion status.
* @since Android 1.0
*/
public void setClassAssertionStatus(String cname, boolean enable) {
return;
}

/**
* Sets the assertion status of the package with the specified name.
* <p>
* <strong>Note: </strong>This method does nothing in the Android reference
* implementation.
* </p>
*
* @param pname
* the name of the package for which to set the assertion status.
* @param enable
* the new assertion status.
* @since Android 1.0
*/
public void setPackageAssertionStatus(String pname, boolean enable) {
return;
}

/**
* Sets the default assertion status for this class loader.
* <p>
* <strong>Note: </strong>This method does nothing in the Android reference
* implementation.
* </p>
*
* @param enable
* the new assertion status.
* @since Android 1.0
*/
public void setDefaultAssertionStatus(boolean enable) {
return;
}

/**
* Sets the default assertion status for this class loader to {@code false}
* and removes any package default and class assertion status settings.
* <p>
* <strong>Note:</strong> This method does nothing in the Android reference
* implementation.
* </p>
*
* @since Android 1.0
*/
public void clearAssertionStatus() {
return;
}

/**
* Returns the assertion status of the named class Returns the assertion
* status of the class or nested class if it has been set. Otherwise returns
* the assertion status of its package or superpackage if that has been set.
* Otherwise returns the default assertion status. Returns 1 for enabled and
* 0 for disabled.
*
* @return the assertion status.
* @param cname
* the name of class.
*/
boolean getClassAssertionStatus(String cname) {
return false;
}

/**
* Returns the assertion status of the named package Returns the assertion
* status of the named package or superpackage if that has been set.
* Otherwise returns the default assertion status. Returns 1 for enabled and
* 0 for disabled.
*
* @return the assertion status.
* @param pname
* the name of package.
*/
boolean getPackageAssertionStatus(String pname) {
return false;
}

/**
* Returns the default assertion status
*
* @return the default assertion status.
*/
boolean getDefaultAssertionStatus() {
return false;
}
}

/*
* Provides a helper class that combines two existing URL enumerations into one.
* It is required for the getResources() methods. Items are fetched from the
* first enumeration until it's empty, then from the second one.
*/
class TwoEnumerationsInOne implements Enumeration<URL> {

private Enumeration<URL> first;

private Enumeration<URL> second;

public TwoEnumerationsInOne(Enumeration<URL> first, Enumeration<URL> second) {
this.first = first;
this.second = second;
}

public boolean hasMoreElements() {
return first.hasMoreElements() || second.hasMoreElements();
}

public URL nextElement() {
if (first.hasMoreElements()) {
return first.nextElement();
} else {
return second.nextElement();
}
}

}

/**
* Provides an explicit representation of the boot class loader. It sits at the
* head of the class loader chain and delegates requests to the VM's internal
* class loading mechanism.
*/
class BootClassLoader extends ClassLoader {

static BootClassLoader instance;

public static BootClassLoader getInstance() {
if (instance == null) {
instance = new BootClassLoader();
}

return instance;
}

public BootClassLoader() {
super(null, true);
}

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
return VMClassLoader.loadClass(name, false);
}

@Override
protected URL findResource(String name) {
return VMClassLoader.getResource(name);
}

@SuppressWarnings("unused")
@Override
protected Enumeration<URL> findResources(String resName) throws IOException {
Enumeration<URL> result = VMClassLoader.getResources(resName);

// VMClassLoader doesn't keep the contract for getResources()
if (result == null) {
result = EmptyEnumeration.getInstance();
}

return result;
}

/**
* Returns package information for the given package. Unfortunately, the
* Android BootClassLoader doesn't really have this information, and as a
* non-secure ClassLoader, it isn't even required to, according to the spec.
* Yet, we want to provide it, in order to make all those hopeful callers of
* {@code myClass.getPackage().getName()} happy. Thus we construct a Package
* object the first time it is being requested and fill most of the fields
* with dummy values. The Package object is then put into the ClassLoader's
* Package cache, so we see the same one next time. We don't create Package
* objects for null arguments or for the default package.
* <p>
* There a limited chance that we end up with multiple Package objects
* representing the same package: It can happen when when a package is
* scattered across different JAR files being loaded by different
* ClassLoaders. Rather unlikely, and given that this whole thing is more or
* less a workaround, probably not worth the effort.
*/
@Override
protected Package getPackage(String name) {
if (name != null && !"".equals(name)) {
synchronized (this) {
Package pack = super.getPackage(name);

if (pack == null) {
pack = definePackage(name, "Unknown", "0.0", "Unknown", "Unknown", "0.0",
"Unknown", null);
}

return pack;
}
}

return null;
}

@Override
public URL getResource(String resName) {
return findResource(resName);
}

@Override
protected Class<?> loadClass(String className, boolean resolve)
throws ClassNotFoundException {
Class<?> clazz = findLoadedClass(className);

if (clazz == null) {
clazz = findClass(className);
}

return clazz;
}

@Override
public Enumeration<URL> getResources(String resName) throws IOException {
return findResources(resName);
}
}

/**
* TODO Open issues - Missing / empty methods - Signer stuff - Protection
* domains - Assertions
*/


[/code]


DexClassLoader.java

/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package dalvik.system;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.zip.ZipFile;
import java.net.MalformedURLException;

import dalvik.system.DexFile;

/**
* Provides a simple {@link ClassLoader} implementation that operates on a
* list of jar/apk files with classes.dex entries. The directory that
* holds the optimized form of the files is specified explicitly. This
* can be used to execute code not installed as part of an application.
*
* The best place to put the optimized DEX files is in app-specific
* storage, so that removal of the app will automatically remove the
* optimized DEX files. If other storage is used (e.g. /sdcard), the
* app may not have an opportunity to remove them.
*/
public class DexClassLoader extends ClassLoader {

private static final boolean VERBOSE_DEBUG = false;

/* constructor args, held for init */
private final String mRawDexPath;
private final String mRawLibPath;
private final String mDexOutputPath;

private boolean mInitialized;

/*
* Parallel arrays for jar/apk files.
*
* (could stuff these into an object and have a single array;
* improves clarity but adds overhead)
*/
private File[] mFiles; // source file Files, for rsrc URLs
private ZipFile[] mZips; // source zip files, with resources
private DexFile[] mDexs; // opened, prepped DEX files

/*
* Native library path.
*/
private String[] mLibPaths;


/**
* Creates a {@code DexClassLoader} that finds interpreted and native
* code. Interpreted classes are found in a set of DEX files contained
* in Jar or APK files.
*
* The path lists are separated using the character specified by
* the "path.separator" system property, which defaults to ":".
*
* @param dexPath
* the list of jar/apk files containing classes and resources
* @param dexOutputDir
* directory where optimized DEX files should be written
* @param libPath
* the list of directories containing native libraries; may be null
* @param parent
* the parent class loader
*/
public DexClassLoader(String dexPath, String dexOutputDir, String libPath,
ClassLoader parent) {

super(parent);

if (dexPath == null || dexOutputDir == null)
throw new NullPointerException();

mRawDexPath = dexPath;
mDexOutputPath = dexOutputDir;
mRawLibPath = libPath;
}

/**
* Complete initialization on first use of the class loader.
*/
private synchronized void ensureInit() {
if (mInitialized) {
return;
}

String[] dexPathList;

mInitialized = true;

dexPathList = mRawDexPath.split(":");
int length = dexPathList.length;

//System.out.println("DexClassLoader: " + dexPathList);
mFiles = new File[length];
mZips = new ZipFile[length];
mDexs = new DexFile[length];

/* open all Zip and DEX files up front */
for (int i = 0; i < length; i++) {
//System.out.println("My path is: " + dexPathList[i]);
File pathFile = new File(dexPathList[i]);
mFiles[i] = pathFile;

if (pathFile.isFile()) {
try {
mZips[i] = new ZipFile(pathFile);
} catch (IOException ioex) {
// expecting IOException and ZipException
System.out.println("Failed opening '" + pathFile
+ "': " + ioex);
//ioex.printStackTrace();
}

/* we need both DEX and Zip, because dex has no resources */
try {
String outputName =
generateOutputName(dexPathList[i], mDexOutputPath);
mDexs[i] = DexFile.loadDex(dexPathList[i], outputName, 0);
} catch (IOException ioex) {
// might be a resource-only zip
System.out.println("Failed loadDex '" + pathFile
+ "': " + ioex);
}
} else {
if (VERBOSE_DEBUG)
System.out.println("Not found: " + pathFile.getPath());
}
}

/*
* Prep for native library loading.
*/
String pathList = System.getProperty("java.library.path", ".");
String pathSep = System.getProperty("path.separator", ":");
String fileSep = System.getProperty("file.separator", "/");

if (mRawLibPath != null) {
if (pathList.length() > 0) {
pathList += pathSep + mRawLibPath;
}
else {
pathList = mRawLibPath;
}
}

mLibPaths = pathList.split(pathSep);
length = mLibPaths.length;

// Add a '/' to the end so we don't have to do the property lookup
// and concatenation later.
for (int i = 0; i < length; i++) {
if (!mLibPaths[i].endsWith(fileSep))
mLibPaths[i] += fileSep;
if (VERBOSE_DEBUG)
System.out.println("Native lib path " +i+ ": " + mLibPaths[i]);
}
}

/**
* Convert a source path name and an output directory to an output
* file name.
*/
private static String generateOutputName(String sourcePathName,
String outputDir) {

StringBuilder newStr = new StringBuilder(80);

/* start with the output directory */
newStr.append(outputDir);
if (!outputDir.endsWith("/"))
newStr.append("/");

/* get the filename component of the path */
String sourceFileName;
int lastSlash = sourcePathName.lastIndexOf("/");
if (lastSlash < 0)
sourceFileName = sourcePathName;
else
sourceFileName = sourcePathName.substring(lastSlash+1);

/*
* Replace ".jar", ".zip", whatever with ".dex". We don't want to
* use ".odex", because the build system uses that for files that
* are paired with resource-only jar files. If the VM can assume
* that there's no classes.dex in the matching jar, it doesn't need
* to open the jar to check for updated dependencies, providing a
* slight performance boost at startup. The use of ".dex" here
* matches the use on files in /data/dalvik-cache.
*/
int lastDot = sourceFileName.lastIndexOf(".");
if (lastDot < 0)
newStr.append(sourceFileName);
else
newStr.append(sourceFileName, 0, lastDot);
newStr.append(".dex");

if (VERBOSE_DEBUG)
System.out.println("Output file will be " + newStr.toString());
return newStr.toString();
}

/**
* Finds a class. This method is called by {@code loadClass()} after the
* parent ClassLoader has failed to find a loaded class of the same name.
*
* @param name
* The name of the class to search for, in a human-readable form
* like "java.lang.String" or "java.net.URLClassLoader$3$1".
* @return the {@link Class} object representing the class
* @throws ClassNotFoundException
* if the class cannot be found
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
ensureInit();

if (VERBOSE_DEBUG)
System.out.println("DexClassLoader " + this
+ ": findClass '" + name + "'");

byte[] data = null;
int length = mFiles.length;

for (int i = 0; i < length; i++) {
if (VERBOSE_DEBUG)
System.out.println(" Now searching: " + mFiles[i].getPath());

if (mDexs[i] != null) {
String slashName = name.replace('.', '/');
Class clazz = mDexs[i].loadClass(slashName, this);
if (clazz != null) {
if (VERBOSE_DEBUG)
System.out.println(" found");
return clazz;
}
}
}

throw new ClassNotFoundException(name + " in loader " + this);
}

/**
* Finds a resource. This method is called by {@code getResource()} after
* the parent ClassLoader has failed to find a loaded resource of the same
* name.
*
* @param name
* The name of the resource to find
* @return the location of the resource as a URL, or {@code null} if the
* resource is not found.
*/
@Override
protected URL findResource(String name) {
ensureInit();

int length = mFiles.length;

for (int i = 0; i < length; i++) {
File pathFile = mFiles[i];
ZipFile zip = mZips[i];

if (zip.getEntry(name) != null) {
if (VERBOSE_DEBUG)
System.out.println(" found " + name + " in " + pathFile);
try {
// File.toURL() is compliant with RFC 1738 in always
// creating absolute path names. If we construct the
// URL by concatenating strings, we might end up with
// illegal URLs for relative names.
return new URL("jar:" + pathFile.toURL() + "!/" + name);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
}

if (VERBOSE_DEBUG)
System.out.println(" resource " + name + " not found");

return null;
}

/**
* Finds a native library. This method is called after the parent
* ClassLoader has failed to find a native library of the same name.
*
* @param libname
* The name of the library to find
* @return the complete path of the library, or {@code null} if the library
* is not found.
*/
@Override
protected String findLibrary(String libname) {
ensureInit();

String fileName = System.mapLibraryName(libname);
for (int i = 0; i < mLibPaths.length; i++) {
String pathName = mLibPaths[i] + fileName;
File test = new File(pathName);

if (test.exists()) {
if (VERBOSE_DEBUG)
System.out.println(" found " + libname);
return pathName;
}
}

if (VERBOSE_DEBUG)
System.out.println(" library " + libname + " not found");
return null;
}

/**
* Returns package information for the given package. Unfortunately, the
* DexClassLoader doesn't really have this information, and as a non-secure
* ClassLoader, it isn't even required to, according to the spec. Yet, we
* want to provide it, in order to make all those hopeful callers of
* <code>myClass.getPackage().getName()</code> happy. Thus we construct a
* Package object the first time it is being requested and fill most of the
* fields with dummy values. The Package object is then put into the
* ClassLoader's Package cache, so we see the same one next time. We don't
* create Package objects for null arguments or for the default package.
* <p>
* There a limited chance that we end up with multiple Package objects
* representing the same package: It can happen when when a package is
* scattered across different JAR files being loaded by different
* ClassLoaders. Rather unlikely, and given that this whole thing is more or
* less a workaround, probably not worth the effort.
*
* @param name
* the name of the class
* @return the package information for the class, or {@code null} if there
* is not package information available for it
*/
@Override
protected Package getPackage(String name) {
if (name != null && !"".equals(name)) {
synchronized(this) {
Package pack = super.getPackage(name);

if (pack == null) {
pack = definePackage(name, "Unknown", "0.0", "Unknown",
"Unknown", "0.0", "Unknown", null);
}

return pack;
}
}

return null;
}
}



PathClassLoader.java
[code="java"]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package dalvik.system;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.net.MalformedURLException;

import dalvik.system.DexFile;

/**
* Provides a simple {@link ClassLoader} implementation that operates on a list
* of files and directories in the local file system, but does not attempt to
* load classes from the network. Android uses this class for its system class
* loader and for its application class loader(s).
*
* @since Android 1.0
*/
public class PathClassLoader extends ClassLoader {

private final String path;
private final String libPath;

private boolean initialized;

private String[] mPaths;
private File[] mFiles;
private ZipFile[] mZips;
private DexFile[] mDexs;
private String[] mLibPaths;

/**
* Creates a {@code PathClassLoader} that operates on a given list of files
* and directories. This method is equivalent to calling
* {@link #PathClassLoader(String, String, ClassLoader)} with a
* {@code null} value for the second argument (see description there).
*
* @param path
* the list of files and directories
*
* @param parent
* the parent class loader
*/
public PathClassLoader(String path, ClassLoader parent) {
this(path, null, parent);
}

/**
* Creates a {@code PathClassLoader} that operates on two given lists of
* files and directories. The entries of the first list should be one of the
* following:
* <ul>
* <li>Directories containing classes or resources.
* <li>JAR/ZIP/APK files, possibly containing a "classes.dex" file.
* <li>"classes.dex" files.
* </ul>
* The entries of the second list should be directories containing native
* library files. Both lists are separated using the character specified by
* the "path.separator" system property, which, on Android, defaults to ":".
*
* @param path
* the list of files and directories containing classes and
* resources
*
* @param libPath
* the list of directories containing native libraries
*
* @param parent
* the parent class loader
*/
public PathClassLoader(String path, String libPath, ClassLoader parent) {
super(parent);

if (path == null)
throw new NullPointerException();

this.path = path;
this.libPath = libPath;
}

private synchronized void ensureInit() {
if (initialized) {
return;
}

initialized = true;

mPaths = path.split(":");
int length = mPaths.length;

//System.out.println("PathClassLoader: " + mPaths);
mFiles = new File[length];
mZips = new ZipFile[length];
mDexs = new DexFile[length];

boolean wantDex =
System.getProperty("android.vm.dexfile", "").equals("true");

/* open all Zip and DEX files up front */
for (int i = 0; i < length; i++) {
//System.out.println("My path is: " + mPaths[i]);
File pathFile = new File(mPaths[i]);
mFiles[i] = pathFile;

if (pathFile.isFile()) {
try {
mZips[i] = new ZipFile(pathFile);
}
catch (IOException ioex) {
// expecting IOException and ZipException
//System.out.println("Failed opening '" + pathFile + "': " + ioex);
//ioex.printStackTrace();
}
if (wantDex) {
/* we need both DEX and Zip, because dex has no resources */
try {
mDexs[i] = new DexFile(pathFile);
}
catch (IOException ioex) {}
}
}
}

/*
* Prep for native library loading.
*/
String pathList = System.getProperty("java.library.path", ".");
String pathSep = System.getProperty("path.separator", ":");
String fileSep = System.getProperty("file.separator", "/");

if (libPath != null) {
if (pathList.length() > 0) {
pathList += pathSep + libPath;
}
else {
pathList = libPath;
}
}

mLibPaths = pathList.split(pathSep);
length = mLibPaths.length;

// Add a '/' to the end so we don't have to do the property lookup
// and concatenation later.
for (int i = 0; i < length; i++) {
if (!mLibPaths[i].endsWith(fileSep))
mLibPaths[i] += fileSep;
if (false)
System.out.println("Native lib path: " + mLibPaths[i]);
}
}

/**
* Finds a class. This method is called by {@code loadClass()} after the
* parent ClassLoader has failed to find a loaded class of the same name.
*
* @param name
* The "binary name" of the class to search for, in a
* human-readable form like "java.lang.String" or
* "java.net.URLClassLoader$3$1".
* @return the {@link Class} object representing the class
* @throws ClassNotFoundException
* if the class cannot be found
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{
ensureInit();

//System.out.println("PathClassLoader " + this + ": findClass '" + name + "'");

byte[] data = null;
int length = mPaths.length;

for (int i = 0; i < length; i++) {
//System.out.println("My path is: " + mPaths[i]);

if (mDexs[i] != null) {
Class clazz = mDexs[i].loadC
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值