Android uses APT Technology to generate code at compile time

Apt (short for annotation processing tool) can parse annotations during code compilation and generate new java files to reduce manual code input. Many mainstream libraries now use apt, such as dagger2, butterknife, eventbus3, etc. we should keep up with the trend and keep pace with the times! (ง • ̀_•́) ง

Next, a simple view injection project, viewfinder, is used to introduce apt related content. Two annotations like @ bindview and @ onclick in butterknife are simply implemented.

Project address: https://github.com/brucezz/ViewFinder

The approximate project structure is as follows:

Achieve goals

In ordinary Android projects, a large number of interfaces are written, so some code will be written repeatedly, such as:

If you write such long and mindless code every day, can you play happily. Therefore, I intend to replace this repetitive work through the viewfinder project, which only needs to be annotated simply. Annotate through the control ID, and @ onclick can annotate the same method for multiple controls. Like this:

Define annotation

Create a module viewfinder annotation of type Java library and define the annotations required by the project.

Two annotations @ bindview and @ onclick are required in the viewfinder. The implementation is as follows:

@Bindview needs to annotate member variables and receive an int type parameter@ Onclick needs to annotate the method and receive a set of int type parameters, which is equivalent to specifying click response events for a set of views.

Write API

Create a module viewfinder of type Android library. Define the API in this module, that is to determine how to let others use our project.

First, you need an API main entry to provide direct calls to static methods, such as:

ViewFinder. inject(this);

At the same time, you need to provide overloaded injection methods for different targets (such as activity, fragment, view, etc.), and finally call the inject () method. There are three parameters:

So what is written in the inject () method?

First, we need an interface finder, then generate a corresponding internal class for each annotation class and implement the interface, and then implement the specific injection logic. In the inject () method, we first find the Finder implementation class corresponding to the caller, then invoke its internal logic to achieve the purpose of injection.

The interface finder is designed as follows:

For example, generate mainactivity $$finder for mainactivity, initialize its annotated view and set click events, which is basically the same as the repetitive code we usually write.

Well, all annotation classes have an internal class called XX $$finder. We first annotate the class name of the class to get the class object of its corresponding internal class, then instantiate to get the specific object and call the injection method.

In addition, a little reflection is used in the code, so in order to improve efficiency and avoid looking for finder objects every time. Here, a map is used to cache the objects found for the first time, and then directly take them from the map.

At this point, the design of the API module is basically completed. The next step is to generate finder internal classes for each annotation class through the annotation processor.

Create annotation processor

Create a module viewfinder compiler of type Java library to implement an annotation processor.

This module needs to add some dependencies:

Let's create our processor viewfinderprocessor.

Annotate the processor with @ autoservice to automatically generate configuration information.

In init (), you can get some useful tool classes.

Return the collection of annotations to process in the getsupportedannotationtypes () method.

Return the Java version in the getsupportedsourceversion() method.

The writing methods of these methods are basically fixed, and the main play is the process () method.

Here are some concepts related to element, which will be used later.

Element element. Each part of the source code is a specific element type, representing packages, classes, methods, etc. see the demo for details.

These element elements are equivalent to the DOM tree in XML. You can access its parent or child elements through an element.

element. getEnclosingElement();// Gets the parent element element getEnclosedElements();// Get child element

The whole process of annotation processor is no different from ordinary Java programs. We can use object-oriented ideas and design patterns to encapsulate the relevant logic into the model, making the process clearer and simpler. The annotation member variables, click methods and the entire annotation class are encapsulated into different models.

It mainly checks the element type during initialization, then obtains the annotation value, and provides several get methods. Onclickmethod encapsulation is similar.

Annotatedclass represents an annotation class with two lists containing annotated member variables and methods. In the generatefinder () method, the code is generated by using the API of javapool according to the template designed in the previous section. There is no special posture in this part. Just follow the javapool document. The document is written in detail.

There are many places where object types need to be used. Common types can be used

ClassName get(String packageName,String simpleName,String... simpleNames)

Pass in the package name, class name and internal class name to get the desired type (refer to the typeutil class in the project).

If generics are used, parameterized typename get (classname, rawtype, typename... Typearguments) can be used

Just pass in concrete classes and generic types.

After these models are determined, the process () method is very refreshing. Use the roundenvironment parameter to query the element marked by a specific annotation, then parse it into a specific model, and finally generate the code and output it to the file.

First, analyze the annotation element and put it into the corresponding annotation class object, and finally call the method to generate the file. Some verification codes will be added to the model code to judge whether the annotation element is reasonable and whether the data is normal, and then an exception will be thrown. After receiving it, the processor can print out an error prompt, and then directly return true to end the processing.

So far, the annotation processor is basically completed. Refer to the project code for details.

Actual project use

Create a module sample, an ordinary Android module, to demonstrate the use of viewfinder.

Under the whole project, build Add in gradle

classpath 'com. neenbedankt. gradle. plugins:android-apt:1.8'

Then, under the sample module, click build Add in gradle

apply plugin: 'com. neenbedankt. android-apt'

Add dependencies at the same time:

Then you can create a layout and add a few controls to experience annotation.

At this time, build the project, you can see the generated mainactivity $$finder class, and then run the project. After each annotation change, just build the project.

all done ~

This project is also a toy level apt project to learn how to write apt projects. It seems that the apt project is more about how to design the architecture, how to call between classes, what kind of code to generate, and what kind of API to call. Finally, the annotation processor is used to parse the annotation, and then javapool is used to generate specific code.

Thinking is more important than implementation, and design is more ingenious than code.

reference resources

Through this article, I hope to help you develop Android applications. Thank you for your support for this site!

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>