- Mirror: Easy Reflection for Java and Android
- What is Mirror?
- Example: Using the method doCoolStuff() from class PrivateCoolClass
- Download
- What’s next?
- Notes:
- Learn Java for Android Development: Reflection Basics
- What You’ll Need
- Why Use Reflection?
- Inspecting Classes
- Inspecting the Constructors Available Within a Class
- Inspecting the Fields Available Within a Class
- Inspecting the Methods Available Within a Class
- Inspecting Inner Classes
- Inspecting Member Modifiers
- Inspecting Class Metadata
- Reflection: Handy for Debugging
- Why Not To Use Reflection
- Wrapping Up
- Using Reflection in Android
- What is Reflection?
- Using reflection to set mock Android dependencies
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:
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:
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:
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:
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:
- As an alternative you can also have a look at FEST-Reflect
- Save sharks!
Liked what you saw? Hit that ❤ and follow me 🤓
Источник
Learn Java for Android Development: Reflection Basics
In this tutorial, you’ll become familiar with the concept of Java reflection: the ability of a class or object to examine details about its own implementation programmatically.
Android applications are written in the Java, a programming language that supports reflection—the ability of an object to examine itself. In this tutorial, you’ll learn the basics of Java reflection, including how to inspect the methods and fields of a given class, check for the availability of specific methods, and other practical tasks you may need to use when developing for different versions of the Android SDK.
What You’ll Need
Technically, you don’t need any tools to complete this tutorial but you will certainly need them to develop Android applications.
To develop Android applications (or any Java applications, for that matter), you need a development environment to write and build applications. Eclipse is a very popular development environment (IDE) for Java and the preferred IDE for Android development. It’s freely available for Windows, Mac, and Linux operating systems.
For complete instructions on how to install Eclipse (including which versions are supported) and the Android SDK, see the Android developer website.
Why Use Reflection?
Reflection gives developers the flexibility to inspect and determine API characteristics at runtime, instead of compile time. Within the security constraints imposed by Java (e.g. use of public, protected, private), you can then construct objects, access fields, and invoke methods dynamically. The Java Reflection APIs are available as part of the java.lang.reflect package, which is included within the Android SDK for developers to use.
So what does this have to do with Android development? Well, with each new version of the Android SDK, classes, interfaces, methods, etc. are added, updated, and (less frequently) removed. However, Android developers often want to target devices running different versions of Android with a simple application package. To do this, Android developers may use reflection techniques to determine, at runtime, if a specific class or method is available before trying to use it. This allows the developer to leverage new APIs where available while still supporting the older devices—all in the same application.
Inspecting Classes
Java classes are represented at runtime using the Class (java.lang.Class) class. This class provides the starting point for all reflection APIs. Within this class, you’ll find many methods for inspecting different aspects of a class, such as its fields, constructors, methods, permissions, and more. You can also use the Class method called forName() to load a non-primitive class (e.g. not int, but Integer) by name dynamically at runtime, instead of at compile time:
The class (in this case, NotificationManager) need not have the corresponding import statement in your code; you are not compiling in this class into your application. Instead, the class loader will load the class dynamically at runtime, if possible. You can then inspect this Class object and use the reflection techniques described in the rest of this tutorial.
Inspecting the Constructors Available Within a Class
You can inspect the constructors available within a given Class. To get just the constructors that are publicly available, use getConstructors(). However, if you want to inspect those methods specifically declared within the class, whether they are public or not, use getDeclaredConstructors() instead. Both methods return an array of Constructor (java.lang.reflect.Constructor) objects.
For example, the following code iterates through the declared constructors of a class:
Once you have a valid Constructor object, you can inspect its parameters and even declare a new instance of the class using that constructor with the newInstance() method.
Inspecting the Fields Available Within a Class
You can inspect the fields (or attributes) available within a given Class. To get just the methods that are publicly available, including inherited fields, use getFields(). However, if you want to inspect those fields specifically declared within the class (and not inherited ones), whether they are public or not, use getDeclaredFields() instead. Both methods return an array of Field (java.lang.reflect.Field) objects.
For example, the following code iterates through the declared fields of a class:
You can also check for a specific public field by name using the getField() method. For example, to check for the EXTRA_CHANGED_PACKAGE_LIST field of the Intent class (which was added in API Level 8, or Android 2.2), you could use:
Once you have a valid Field object, you can get its name using the toGenericString() method. If you have the appropriate permissions, you can also access the value of that class field using the appropriate get() and set() methods.
Inspecting the Methods Available Within a Class
You can inspect the methods available within a given Class. To get just the methods that are publicly available, including inherited methods, use getMethods(). However, if you want to inspect those methods specifically declared within the class (without inherited ones), whether they are public or not, use getDeclaredMethods() instead. Both methods return an array of Method (java.lang.reflect.Method) objects.
For example, the following code iterates through the declared methods of a class:
Once you have a valid Method object, you can get its name using the toGenericString() method. You can also examine the parameters used by the method and the exceptions it can throw. Finally, if you have the appropriate permissions, you can also call the method using the invoke() method.
Inspecting Inner Classes
You can inspect the inner classes defined within a Class using getDeclaredClasses() method. This method will return an array of Class (java.lang.class) objects declared within the parent class. These classes can then be inspected like any other.
Inspecting Member Modifiers
You can also inspect the flags and security settings—called modifiers—associated with a given Class, Field, or Method using the getModifiers() method. Interesting modifiers include whether the component is public, private, protected, abstract, final, or static (amongst others).
For example, the following code checks the security modifiers of a class:
Keep in mind that you cannot dynamically access or invoke any class, method, or field using reflection that you would not normally be able to access at compile-time. In other words, regular class security is still applied at runtime.
Inspecting Class Metadata
You can also inspect the metadata—called annotations—associated with a given class, field or method using the getAnnotations() method. Interesting metadata associated with a class might include information about deprecation, warnings, and overrides, among other things.
For example, the following code checks the metadata available for the AbsoluteLayout class. Since this class was deprecated in Android 1.5, one of the annotations returned is @java.lang.Deprecated() when this code is run on Android 2.2:
Similarly, you could simply check for the existence of a specific annotation, such as deprecation, by its type:
Reflection: Handy for Debugging
You can also use reflection to assist with debugging. For example, you might want to use the class keyword to access the underlying class data for a given type:
You can also get class information from a variable instance using the getClass() method of the Object class (which is therefore inherited by all classes in Java):
If you want to check the class of a variable, using instanceof is more appropriate. See the previous tutorial on instanceof for more details.
Similarly, you might want to use the getClass() method with the this keyword to check the name of the class you’re currently in and include this information as part of your debug logging to LogCat:
Why Not To Use Reflection
As you’ve seen, reflection can be used to great effect, especially when you are unsure if a specific class or method is available at compile time. Reflection does, however, have some drawbacks, including reduced performance and the loss of the strong typing and safe coding practices enforced at compile time. It’s best to use reflection sparingly, but do use it when needed.
Wrapping Up
Reflection is a powerful tool that Java developers can use to explore packages and APIs programmatically at runtime. While reflection operations come at a cost, they give the developer the flexibility that is sometimes essential for getting the job done. Android developers frequently use these simple reflection techniques to test for the availability of specific classes, interfaces, methods, and fields at runtime, enabling them to support different versions.
Источник
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. T he 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:
Источник
Using reflection to set mock Android dependencies
Local unit tests on Android run against a special android.jar that does not include the real framework code. This version of android.jar is intended to me mocked. Calling a method from your tests that has not been mocked will throw an exception.
The following simple test creates an instance of MainActivity then attempts to call onCreate(null) before asserting the activity is not null.
However upon calling onCreate(null) the framework immediately throws an exception if the method has not been explicitly mocked.
Rather than mocking every framework method that could possibly be invoked while running out tests we can instead configure the test environment to return default values — usually 0 or null .
Adding the above testOptions block to the app module build.gradle tells the modified version of the Android framework to return default values.
This gets us a little bit further but unfortunately we run into a new problem. Since the default return value for activity.getWindow() is null the support library throws a NullPointerException when it tries to call mWindow.getCallback() .
One possible solution would be to use a partial mock of our class under test to return a test Window that is not null . This is a slippery slope however. If you start changing the behavior of your class under test then what you are really testing — the production code or your modified version?
Instead of a partial mock let’s use what feels like the lesser of two evils and fix our issue using reflection. Looking at the implementation of AppCompatActivity.getDelegate() it does not attempt to make a new delegate if it already has an instance. So let’s give it one.
There is one other problem. If the value of Build.VERSION.SDK_INT is less than 11 then BaseFragmentActivityGingerbread will attempt to call getLayoutInflater().getFactory() which will also throw a NullPointerException . To fix this problem we can also use reflection to set the SDK level.
Now we are able to run MainActivity.onCreate() from our tests.
To simplify things you could create helper methods for both of these reflection snippets and call them from your setUp() method before each test.
Using this strategy of mocking plus reflection can help you work around pesky framework dependencies in your unit test environment without resorting to a partial mock of your class under test.
Источник