AndroidJarLoaderpublic class AndroidJarLoader extends ClassLoader implements IAndroidClassLoaderCustom class loader able to load a class from the SDK jar file. |
Fields Summary |
---|
private String | mOsFrameworkLocation | private final HashMap | mEntryCacheA cache for binary data extracted from the zip | private final HashMap | mClassCacheA cache for already defined Classes |
Constructors Summary |
---|
public AndroidJarLoader(String osFrameworkLocation)Creates the class loader by providing the os path to the framework jar archive
super();
mOsFrameworkLocation = osFrameworkLocation;
|
Methods Summary |
---|
private java.lang.Class | defineAndCacheClass(java.lang.String name, byte[] data)Defines a class based on its binary data and caches the resulting class object.
Class<?> cached_class;
cached_class = defineClass(null, data, 0, data.length);
if (cached_class != null) {
// Add new class to the cache class and remove it from the zip entry data cache
mClassCache.put(name, cached_class);
mEntryCache.remove(name);
}
return cached_class;
| private java.lang.String | entryPathToClassName(java.lang.String entryPath)Helper method that converts a Zip entry path into a corresponding
Java full qualified binary class name.
F.ex, this converts "com/my/package/Foo.class" into "com.my.package.Foo".
return entryPath.replaceFirst("\\.class$", "").replaceAll("[/\\\\]", "."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
| protected java.lang.Class | findClass(java.lang.String name)Finds the class with the specified binary name.
{@inheritDoc}
try {
// try to find the class in the cache
Class<?> cached_class = mClassCache.get(name);
if (cached_class == ClassNotFoundException.class) {
// we already know we can't find this class, don't try again
throw new ClassNotFoundException(name);
} else if (cached_class != null) {
return cached_class;
}
// if not found, look it up and cache it
byte[] data = loadClassData(name);
if (data != null) {
return defineAndCacheClass(name, data);
} else {
// if the class can't be found, record a CNFE class in the map so
// that we don't try to reload it next time
mClassCache.put(name, ClassNotFoundException.class);
throw new ClassNotFoundException(name);
}
} catch (ClassNotFoundException e) {
throw e;
} catch (Exception e) {
throw new ClassNotFoundException(e.getMessage());
}
| public java.util.HashMap | findClassesDerivingFrom(java.lang.String packageFilter, java.lang.String[] superClasses)Finds and loads all classes that derive from a given set of super classes.
As a side-effect this will load and cache most, if not all, classes in the input JAR file.
packageFilter = packageFilter.replaceAll("\\.", "/"); //$NON-NLS-1$ //$NON-NLS-2$
HashMap<String, ArrayList<IClassDescriptor>> mClassesFound =
new HashMap<String, ArrayList<IClassDescriptor>>();
for (String className : superClasses) {
mClassesFound.put(className, new ArrayList<IClassDescriptor>());
}
// create streams to read the intermediary archive
FileInputStream fis = new FileInputStream(mOsFrameworkLocation);
ZipInputStream zis = new ZipInputStream(fis);
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
// get the name of the entry and convert to a class binary name
String entryPath = entry.getName();
if (!entryPath.endsWith(AndroidConstants.DOT_CLASS)) {
// only accept class files
continue;
}
if (packageFilter.length() > 0 && !entryPath.startsWith(packageFilter)) {
// only accept stuff from the requested root package.
continue;
}
String className = entryPathToClassName(entryPath);
Class<?> loaded_class = mClassCache.get(className);
if (loaded_class == null) {
byte[] data = mEntryCache.get(className);
if (data == null) {
// Get the class and cache it
long entrySize = entry.getSize();
if (entrySize > Integer.MAX_VALUE) {
throw new InvalidAttributeValueException();
}
data = readZipData(zis, (int)entrySize);
}
loaded_class = defineAndCacheClass(className, data);
}
for (Class<?> superClass = loaded_class.getSuperclass();
superClass != null;
superClass = superClass.getSuperclass()) {
String superName = superClass.getCanonicalName();
if (mClassesFound.containsKey(superName)) {
mClassesFound.get(superName).add(new ClassWrapper(loaded_class));
break;
}
}
}
return mClassesFound;
| public IClassDescriptor | getClass(java.lang.String className)Returns a {@link IAndroidClassLoader.IClassDescriptor} by its fully-qualified name.
try {
return new ClassWrapper(loadClass(className));
} catch (ClassNotFoundException e) {
throw e; // useful for debugging
}
| public java.lang.String | getSource()
return mOsFrameworkLocation;
| private synchronized byte[] | loadClassData(java.lang.String className)Loads a class data from its binary name.
This uses the class binary data that has been preloaded earlier by the preLoadClasses()
method if possible.
byte[] data = mEntryCache.get(className);
if (data != null) {
return data;
}
// The name is a binary name. Something like "android.R", or "android.R$id".
// Make a path out of it.
String entryName = className.replaceAll("\\.", "/") + AndroidConstants.DOT_CLASS; //$NON-NLS-1$ //$NON-NLS-2$
// create streams to read the intermediary archive
FileInputStream fis = new FileInputStream(mOsFrameworkLocation);
ZipInputStream zis = new ZipInputStream(fis);
// loop on the entries of the intermediary package and put them in the final package.
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
// get the name of the entry.
String currEntryName = entry.getName();
if (currEntryName.equals(entryName)) {
long entrySize = entry.getSize();
if (entrySize > Integer.MAX_VALUE) {
throw new InvalidAttributeValueException();
}
data = readZipData(zis, (int)entrySize);
return data;
}
}
return null;
| public void | preLoadClasses(java.lang.String packageFilter, java.lang.String taskLabel, org.eclipse.core.runtime.IProgressMonitor monitor)Pre-loads all class binary data that belong to the given package by reading the archive
once and caching them internally.
This does not actually preload "classes", it just reads the unzipped bytes for a given
class. To obtain a class, one must call {@link #findClass(String)} later.
All classes which package name starts with "packageFilter" will be included and can be
found later.
May throw some exceptions if the framework JAR cannot be read.
// Transform the package name into a zip entry path
String pathFilter = packageFilter.replaceAll("\\.", "/"); //$NON-NLS-1$ //$NON-NLS-2$
SubMonitor progress = SubMonitor.convert(monitor, taskLabel == null ? "" : taskLabel, 100);
// create streams to read the intermediary archive
FileInputStream fis = new FileInputStream(mOsFrameworkLocation);
ZipInputStream zis = new ZipInputStream(fis);
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
// get the name of the entry.
String entryPath = entry.getName();
if (!entryPath.endsWith(AndroidConstants.DOT_CLASS)) {
// only accept class files
continue;
}
// check if it is part of the package to preload
if (pathFilter.length() > 0 && !entryPath.startsWith(pathFilter)) {
continue;
}
String className = entryPathToClassName(entryPath);
if (!mEntryCache.containsKey(className)) {
long entrySize = entry.getSize();
if (entrySize > Integer.MAX_VALUE) {
throw new InvalidAttributeValueException();
}
byte[] data = readZipData(zis, (int)entrySize);
mEntryCache.put(className, data);
}
// advance 5% of whatever is allocated on the progress bar
progress.setWorkRemaining(100);
progress.worked(5);
progress.subTask(String.format("Preload %1$s", className));
}
| private byte[] | readZipData(java.util.zip.ZipInputStream zis, int entrySize)Reads data for the current entry from the zip input stream.
int block_size = 1024;
int data_size = entrySize < 1 ? block_size : entrySize;
int offset = 0;
byte[] data = new byte[data_size];
while(zis.available() != 0) {
int count = zis.read(data, offset, data_size - offset);
if (count < 0) { // read data is done
break;
}
offset += count;
if (entrySize >= 1 && offset >= entrySize) { // we know the size and we're done
break;
}
// if we don't know the entry size and we're not done reading,
// expand the data buffer some more.
if (offset >= data_size) {
byte[] temp = new byte[data_size + block_size];
System.arraycopy(data, 0, temp, 0, data_size);
data_size += block_size;
data = temp;
block_size *= 2;
}
}
if (offset < data_size) {
// buffer was allocated too large, trim it
byte[] temp = new byte[offset];
if (offset > 0) {
System.arraycopy(data, 0, temp, 0, offset);
}
data = temp;
}
return data;
|
|