PathClassLoaderpublic class PathClassLoader extends ClassLoader 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). |
Fields Summary |
---|
private final String | path | private final String | libPath | private boolean | initialized | private String[] | mPaths | private File[] | mFiles | private ZipFile[] | mZips | private dalvik.system.DexFile[] | mDexs | private String[] | mLibPaths |
Constructors Summary |
---|
public PathClassLoader(String path, ClassLoader parent)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).
this(path, null, parent);
| public PathClassLoader(String path, String libPath, ClassLoader 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:
- Directories containing classes or resources.
- JAR/ZIP/APK files, possibly containing a "classes.dex" file.
- "classes.dex" files.
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 ":".
super(parent);
if (path == null)
throw new NullPointerException();
this.path = path;
this.libPath = libPath;
|
Methods Summary |
---|
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 '" + archive + "': " + 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]);
}
| protected java.lang.Class | findClass(java.lang.String name)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.
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) {
String slashName = name.replace('.", '/");
Class clazz = mDexs[i].loadClass(slashName, this);
if (clazz != null)
return clazz;
} else if (mZips[i] != null) {
String fileName = name.replace('.", '/") + ".class";
data = loadFromArchive(mZips[i], fileName);
} else {
File pathFile = mFiles[i];
if (pathFile.isDirectory()) {
String fileName =
mPaths[i] + "/" + name.replace('.", '/") + ".class";
data = loadFromDirectory(fileName);
} else {
//System.out.println("PathClassLoader: can't find '"
// + mPaths[i] + "'");
}
}
/* --this doesn't work in current version of Dalvik--
if (data != null) {
System.out.println("--- Found class " + name
+ " in zip[" + i + "] '" + mZips[i].getName() + "'");
int dotIndex = name.lastIndexOf('.');
if (dotIndex != -1) {
String packageName = name.substring(0, dotIndex);
synchronized (this) {
Package packageObj = getPackage(packageName);
if (packageObj == null) {
definePackage(packageName, null, null,
null, null, null, null, null);
}
}
}
return defineClass(name, data, 0, data.length);
}
*/
}
throw new ClassNotFoundException(name + " in loader " + this);
| protected java.lang.String | findLibrary(java.lang.String libname)Finds a native library. This method is called after the parent
ClassLoader has failed to find a native library of the same name.
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())
return pathName;
}
return null;
| protected java.net.URL | findResource(java.lang.String name)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.
ensureInit();
//java.util.logging.Logger.global.severe("findResource: " + name);
int length = mPaths.length;
for (int i = 0; i < length; i++) {
URL result = findResource(name, i);
if(result != null) {
return result;
}
}
return null;
| private java.net.URL | findResource(java.lang.String name, int i)
File pathFile = mFiles[i];
ZipFile zip = mZips[i];
if (zip != null) {
if (isInArchive(zip, name)) {
//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);
}
}
} else if (pathFile.isDirectory()) {
File dataFile = new File(mPaths[i] + "/" + name);
if (dataFile.exists()) {
//System.out.println(" found resource " + name);
try {
// Same as archive case regarding URL construction.
return dataFile.toURL();
}
catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
} else if (pathFile.isFile()) {
} else {
System.err.println("PathClassLoader: can't find '"
+ mPaths[i] + "'");
}
return null;
| protected java.util.Enumeration | findResources(java.lang.String resName)Finds an enumeration of URLs for the resource with the specified name.
ensureInit();
int length = mPaths.length;
ArrayList<URL> results = new ArrayList<URL>();
for (int i = 0; i < length; i++) {
URL result = findResource(resName, i);
if(result != null) {
results.add(result);
}
}
return new EnumerateListArray<URL>(results);
| protected java.lang.Package | getPackage(java.lang.String name)Returns package information for the given package. Unfortunately, the
PathClassLoader 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
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.
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.
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;
| private boolean | isInArchive(java.util.zip.ZipFile zip, java.lang.String name)
return zip.getEntry(name) != null;
| private byte[] | loadFromArchive(java.util.zip.ZipFile zip, java.lang.String name)
ZipEntry entry;
entry = zip.getEntry(name);
if (entry == null)
return null;
ByteArrayOutputStream byteStream;
InputStream stream;
int count;
/*
* Copy the data out of the stream. Because we got the ZipEntry
* from a ZipFile, the uncompressed size is known, and we can set
* the initial size of the ByteArrayOutputStream appropriately.
*/
try {
stream = zip.getInputStream(entry);
byteStream = new ByteArrayOutputStream((int) entry.getSize());
byte[] buf = new byte[4096];
while ((count = stream.read(buf)) > 0)
byteStream.write(buf, 0, count);
stream.close();
}
catch (IOException ioex) {
//System.out.println("Failed extracting '" + archive + "': " +ioex);
return null;
}
//System.out.println(" loaded from Zip");
return byteStream.toByteArray();
| private byte[] | loadFromDirectory(java.lang.String path)
RandomAccessFile raf;
byte[] fileData;
//System.out.println("Trying to load from " + path);
try {
raf = new RandomAccessFile(path, "r");
}
catch (FileNotFoundException fnfe) {
//System.out.println(" Not found: " + path);
return null;
}
try {
fileData = new byte[(int) raf.length()];
raf.read(fileData);
raf.close();
}
catch (IOException ioe) {
System.err.println("Error reading from " + path);
// swallow it, return null instead
fileData = null;
}
return fileData;
|
|