Skip to content
Mingyuan Xia edited this page Jul 17, 2015 · 3 revisions

We assume that you are fairly familiar with the Java reflection and some basic idea about Dalvik.

An Android application is packed into an APK file, which is actually a zip file. It contains images (res/ and resource.arsc), linkable native libraries (lib/*.so), code (classes.dex), the manifest file (AndroidManifest.xml), signatures (META-INF/) and some other files.

Logically, the code part is composed of classes, each with a list of methods and fields. The core package provides abstractions for Android classes, methods, fields and primitive Java type values, designed with efficiency in mind. It works very much alike Java reflection. PATDroid's core package is designed to provide a superset of Java reflection, while being neutral to the underlying bytecodes.

Here are the list of classes and their short description in the core package:

  • patdroid.core.ClassInfo denotes a Java class, uniquely identified by its fully qualified class name, e.g. "java.lang.String".
  • patdroid.core.ClassDetail denotes the details about a Java class, including its methods, fields, superclass, etc. The point of separating ClassDetail from ClassInfo is to allow the on-demand loading of the details of a class. See below for details.
  • patdroid.core.ClassDetailLoader the base class for loading ClassDetails
  • patdroid.core.ReflectionClassDetailLoader a ClassDetailLoader that loads classes through standard Java reflection
  • patdroid.core.MethodInfo denotes a method, consisting of its name, the qualifier (private, public, static, etc), parameter types (ClassInfos), return type and the class that contains this method.
  • patdroid.core.FieldInfo denotes a field within a class, with name, qualifier and type.
  • patdroid.core.PrimitiveInfo is a unified abstraction of Java primitive types (int, char, long, double), basically everything that are not an Object.

Next we explain how these abstractions are related to each other.

Class-related abstractions

ClassInfo is the abstraction for a Java class. ClassInfos are forbidden to be constructed. To obtain the ClassInfo for a given class, use the find-series functions. For example, ClassInfo.findOrCreateClass("java.lang.String") returns the representation for the built-in String class. With a ClassInfo, one can query many things. The javadoc for each API is self-explainable. Note that the difference between the findMethodHere() and the findMethod() is that the former only queries the methods declared within the class in question while the later one also checks parent class(es).

Late-binding

This is a classic problem. Suppose we have two classes Foo and Bar. Foo has a method that takes a Bar parameter and Bar has a method that takes a Foo. If we load Foo, we in terms need to load Bar. But loading Bar then falls back to loading Foo, which forms a circular situation. To solve this, we separate the details about a class (what methods and fields it contains) to ClassDetail. When loading Foo, we just create a ClassInfo for Bar with no details. Then Foo is loaded and we got a ClassInfo for Foo as well as its ClassDetail. Next we load Bar to obtain its ClassDetail. One should avoid using ClassDetail directly and instead access everything from a ClassInfo, because it ensures that if the detail is not found, the correct loading procedure will be started.

Loading strategy

One can design different loaders to load ClassDetails from different sources. For example, we can have one loader loading from an APK file, another loading from Android SDK, and a third one loading from the standard library jar. ClassInfo has a rootDetailLoader which specifies the first loader we should try when we want to load a ClassDetail. If this loader cannot load the detail, then it should fall back to the second loader and so on so forth. The last one in the chain, which should always be the ClassDetailLoader base class, simply throws an exception saying the detail cannot be found.

MethodInfo

There is nothing particular about MethodInfo, simply a collection of everything you saw from a function prototype. One special thing is that everything MethodInfos stored within a ClassInfo should have its myClass pointed back to the ClassInfo. However, if you want to construct an arbitrary function prototype, use MethodInfo.makePrototype. A MethodInfo has the support for computing the hash of the function signature to make it easier to check if two methods have the same prototype. For details, refer to a JVM internal book. Some demos about the APIs:

ClassInfo c = ClassInfo.findClass("java.lang.String");
// String concat(String other);
MethodInfo mprot = MethodInfo.makePrototype("concat", c, new ClassInfo[] {c}, 0);
MethodInfo m = c.findMethodHere(mprot);
// m.hasSameSignature(mprot) == true
c.findMethodsHere("indexOf") // return two versions
// int indexOf(String str)
// int indexOf(String str, int fromIndex)
c.findMethodHere("valueOf", c, new ClassInfo[] {ClassInfo.primitiveDouble}, Modifier.STATIC);
// finds: static String	valueOf(double d)

FieldInfo

Again, this thing has nothing special.

PrimitiveInfo

We make this abstraction to unify the representation for the values for Java primitive types. It is type-safe and cast-safe. The below code snippet should be self-explainable:

PrimitiveInfo aLong = new PrimitiveInfo(120l);
PrimitiveInfo aFloat = new PrimitiveInfo(2.4f);
aLong.intValue(); // ASSERT FAILURE: it is a long
aFloat.floatValue(); // == 2.4f
PrimitiveInfo theDouble = aFloat.castTo(ClassInfo.primitiveDouble); // safe cast
theDouble.doubleValue(); // == 2.4
theDouble.floatValue(); // ASSERT FAILURE: it is a double not a float