Using Reflection in Android

When designing an Android Application, one of the important things to remember is the download size of your application. Huge download sizes discourage the users to download and install the application. The factor of size restriction becomes worse if you are designing a library, as developers will not want to bloat their application because of the size of your library.

 

Hence, to keep the library size less, it is important to bundle related functionality together while modularizing the other unrelated functionalities. This allows the developer integrating your library to pick and choose the functionality they want to avail from your library while keeping their application sizes in check.

 

At Haptik, we divided the entire SDK into 3 major modules:

 

1. Core: which will enable the basic chat flows like “Daily Quiz”, “Reminders” etc in the client’s application.


2. Travel: along with the basic chat flow the developers can also enable the functionality to make travel bookings using the Haptik UI flow, by integrating the Travel module of Haptik. (If transactions are needed, Payments module needs to be integrated as well)


3. Payments: Haptik also offers options to make Payments over its platforms, like “Recharge” for phones operators etc.

 

Modularizing the functionalities simultaneously increases the complexity of integrating the library. The developers using your library will have to keep in mind that they should initialize all the modules of the library before using it. Failing to do so, may cause the library to crash.

 

To solve the above problem one of the approaches is using REFLECTION.

 

What is Reflection?

In object-oriented programming languages such as Java, reflection allows inspection of classes, interfaces, fields and methods at runtime without knowing the names of the interfaces, fields, methods at compile time. It also allows instantiation of new objects and invocation of methods (Wikipedia).

 

Let’s understand Reflection in Java with a simple example:

 

// With reflection to instantiate an object
Object foo = Class.forName("complete.classpath.and.Foo").newInstance();
 
// Alternatively you can also use the below:
Object foo = Foo.class.newInstance();
Method m = foo.getClass().getDeclaredMethod("hello", new Class<?>[0]);
m.invoke(foo);

 

Using Java Reflection you can check Java classes at runtime. Checking for classes is often the first thing you do when using Reflection. You can obtain the following information from the class:

 

  • Class Name
  • Class Modifies (public, private, synchronized etc.)
  • Package Info
  • Superclass
  • Implemented Interfaces
  • Constructors
  • Methods
  • Fields
  • Annotations, plus a lot more information related to Java classes.

 

reflection-in-android

For a full list, you should consult the Doc for java.lang.Class.

 

Why use “reflection” to create objects instead of the “new” operator in Java?

 

 

– “new” operator is used to create objects as shown in the below example:

 

 
Foo foo = new Foo();
foo.hello();

 

 

Here, the limitation is that if you use new operator there is no way to define the type of the object at runtime.

 

Let’s see how we can do this using Reflection:

 

Since the type of object needs to be created at the runtime, the availability of the class at the runtime needs to be checked:

 

try {
           Class cls = Class.forName("developer.r47.module1api.Module1FactoryClass");
 
           //this will get the public constructors for the class
           Constructor[] publicConstructors = cls.getConstructors();
 
           //this will get all the declared constructors for the class
           Constructor[] declaredConstructors = cls.getDeclaredConstructors();
 
           //this will return only the public methods
           Method[] methods = cls.getMethods();
           //this will return all the methods declared in the class
           Method[] declaredMethods = cls.getDeclaredMethods();
 
           //this will get all the public fields for the class
           Field[] publicFields = cls.getFields();
           //this will get all the declared fields for the class
           Field[] declaredFields = cls.getDeclaredFields();
 
           Object clsObject = cls.newInstance();
 
           //to get a specific method at the runtime pass the name of the method
           Method method = cls.getDeclaredMethod("getModule1Functionality");
 
           //since the method in question is static the object on which it needs to be called is null
           //else you can pass `clsObject` from above as the parameter to the method below
           Object object = method.invoke(null);
           
           //take action as the module you were looking for exists
       } catch (ClassNotFoundException e) {
           //add your fallback mechanism here
           e.printStackTrace();
       } catch (IllegalAccessException e) {
           //add your fallback mechanism here
           e.printStackTrace();
       } catch (InstantiationException e) {
           //add your fallback mechanism here
           e.printStackTrace();
       } catch (NoSuchMethodException e) {
           e.printStackTrace();
       } catch (InvocationTargetException e) {
           e.printStackTrace();
       }

 

You can explore more details about using reflection to get more properties of a class here.

 

What if the access to a constructor or a field or a method is private?

 

 

You can access a “private” method using:

 

 
constructor.setAccessible(true)
method.setAccessible(true)
field.setAccessible(true)

 

 

Object clsObject = cls.newInstance();
 
           //to get a specific method at the runtime pass the name of the method
           Method method = cls.getDeclaredMethod("getModule1Functionality");
 
           //since the method in question is static the object on which it needs to be called is null
           //else you can pass `clsObject` from above as the parameter to the method below
 
           /*the invoke(Object, obj, Object... args) method takes the object on which the method needs to be
           * invoked as a parameter, and the arguments that are required by the method you are trying to invoke
           * as the second argument*/
 
           /*
           * It throws `NoSuchMethodException` which you will have to handle*/
           Object object = method.invoke(null);

 

So, you can check for the presence of the dependency in the build.gradle and internally initialize the class. If the class is not present you can also have a fallback mechanism.

 

To get access to the code for this project, visit:
https://github.com/rajas47ashtikar92/reflection-sample-app

 

We would love to know what you think about this approach and if there is any better way to tackle the problems of evergrowing sizes of mobile applications/SDKs. In our next blog, we will talk about some other things we did to reduce our android SDK size.