Mirror: Easy Reflection for Java and Android

I’ve been working on Android for a while, and it’s been many years that I must do some tweaking and hacking to call private APIs (using Java Reflection, for good and bad reasons). I got fed up of writing the same twenty lines of code to call one single method. So, during a hackathon organized by Genymobile, my buddy Florent Noël and I decided to try fixing this issue over a weekend. We came up with a solution called Mirror.

What is Mirror?

Mirror is a Java library allowing you to write interfaces with annotations - à la Retrofit - to avoid the pain of the reflection API.

  • Simplicity → Only annotations: no painful API to learn or remember.
  • Readability → Separate the reflection from the code using it.
  • Auto Wrapping → Mirror object methods can return mirror objects.
  • Lightweight → only 12 ko.

Example: Using the method doCoolStuff() from class PrivateCoolClass

Here’s a piece of code using the Java Reflection API:

try {
Class clazz = Class.forName("com.framework.PrivateCoolClass");
Constructor constructor = clazz.getConstructor(String.class);
Object object = constructor.newInstance("Super");
Method method = clazz.getMethod("doCollStuff", int.class);
Object result = method.invoke(object, 42);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}

All exceptions could be caught in one statement but you get the idea. It’s barely readable, all the reflection gets in the way of what you are trying to achieve and you have to remember how to use it every time.

With Mirror, the reflection API is totally invisible. First, we need to declare an interface that will represent the Java class we want to use:

@Class("com.framework.PrivateCoolClass")
public interface CoolClass {
@Constructor void callConstructor(String name);
String doCoolStuff(int value);
}

Some explanations here:

  • The private class is specified by the@Class annotation.
  • As we need to retrieve an instance of the private class at some point, we need a method annotated with@Constructor and the same signature as an existing constructor.
  • Other methods are declared with the exact same signature as the private class.

Once this is done, we are able to do the exact same things as the code above with only a few lines of code:

try {
CoolClass coolClass = Mirror.create(CoolClass.class);
coolClass.callConstructor("Super");
String result = coolClass.doCoolStuff(42);
} catch (MirrorException e) {
e.printStackTrace();
}

If you’d like more information on feature and capabilities, please check the README.

Download

Mirror is available on Jcenter! If you are using Gradle add this line to the dependencies of your module:

compile 'com.genymobile:mirror:1.0.0'

What’s next?

We coded that 2 years ago and just open-sourced it today. We are using it from time to time to debug, prototype or hack things. There is still some work to be done to cover 100% of what is possible to do with reflection and performances can probably be improved greatly. Maybe a 2.0 with code generation or other fancy stuff, we’ll see…

Anyway, feel free to give it a try and let me know if you encounter any issues. Cheers!

Notes:

Liked what you saw? Hit that ❤ and follow me 🤓

Follow Genymobile on Twitter, FacebookGoogle+.