First, Introduction
In the current stage of Android development, annotations are becoming more popular, such as Butterknife,retrofit,dragger,eventbus and so on, which are chosen to be configured using annotations. According to the processing period, the annotations are divided into two types, one is runtime annotations, the other is compile-time annotations, runtime annotations due to performance problems by some people are criticized. The core of compile-time annotations relies on the Annotation processing Tools implementation, which adds annotations on certain code elements, such as types, functions, fields, and so on, and at compile-time the compiler examines the subclasses of Abstractprocessor. And call the type's process function, and then pass all the elements that have added annotations to the process function, so that developers can do the appropriate processing in the compiler, for example, to generate new Java classes based on annotations, which is Eventbus,retrofit, The fundamentals of open source libraries such as Dragger.
The Java API has provided a framework for scanning source code and parsing annotations, and you can inherit the Abstractprocessor class to provide logic for implementing your own parsing annotations. Below we will learn how to generate Java files from compile-time annotations in Android studio.
Second, the concept
Annotation Processoris a tool used in Javac to scan and process annotations at compile time. You can register your own annotation processor for specific annotations.
The annotation processor can generate Java code that consists of the generated Java code
.java
File, but you cannot modify a Java class that already exists (that is, you cannot add a method to an existing class). These generated Java files are Javac compiled along with other common handwriting Java source code.
Abstractprocessor is located under the javax.annotation.processing package, we write our own processor need to inherit it:
public class Lprocessor extends abstractprocessor{ @Override public synchronized void Init ( Processingenvironment processingenvironment) { super.init (processingenvironment); } @Override Public Boolean process (set<? extends Typeelement> Set, roundenvironment roundenvironment) { return false; } @Override public set<string> getsupportedannotationtypes () { return Super.getsupportedannotationtypes (); } @Override public sourceversion getsupportedsourceversion () { return Super.getsupportedsourceversion (); }}
A brief explanation of the above code method
init(ProcessingEnvironment processingEnvironment)
: Each annotation processor class must have an empty constructor. However, there is a special init () method, which is called by the annotation processing tool and enters the processingenviroment parameter. Processingenviroment offers many useful tools such as elements,types and filer. We'll see the details later.
process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment)
: This corresponds to the main function of each processor main (). You are here to write your code for scanning, evaluating and processing annotations, and for generating Java files. Input parameter roundenviroment allows you to query for annotated elements that contain specific annotations. We'll see the details later.
getSupportedAnnotationTypes()
: Here you have to specify which annotation the annotation processor is registered to. Note that its return value is a collection of strings containing the legal full name of the type of annotations the processor wants to process. In other words, here you define which annotations your annotation processor is registered to.
getSupportedSourceVersion()
: Used to specify the Java version you are using. Usually return here sourceversion.latestsupported (). However, if you have enough reason to support only Java 7, you can also return to sourceversion.release_7. Note: After Java 7, you can also use annotations instead of getsupportedannotationtypes () and Getsupportedsourceversion ().
Let's start by creating a Java module lprocessor
@AutoService (processor.class) public class Lprocessor extends Abstractprocessor {private Elements elementutils; @Override public set<string> getsupportedannotationtypes () {//Specify the annotations to be processed return Collections.single Ton (LActivity.class.getCanonicalName ()); } @Override public Boolean process (SET<? extends typeelement> annotations, roundenvironment roundenv) { System.out.println ("Diprocessor"); set<? Extends element> elements = Roundenv.getelementsannotatedwith (Lactivity.class); for (Element element:elements) {//Determine if class typeelement typeelement = (typeelement) Element; list<? Extends element> members = elementutils.getallmembers (typeelement); Methodspec.builder Bindviewmethodspecbuilder = Methodspec.methodbuilder ("BindView"). AddModifiers (Modif Ier. Public, modifier.static). Returns (Typename.void). Addparameter (ClaSsname.get (Typeelement.astype ()), "activity"); for (Element item:members) {LView Diview = item.getannotation (Lview.class); if (Diview = = null) {continue; } bindviewmethodspecbuilder.addstatement (String.Format ("activity.%s = (%s) Activity.findviewbyid (%s)", item. Getsimplename (), Classname.get (Item.astype ()). ToString (), Diview.value ()); } TypeSpec TypeSpec = Typespec.classbuilder ("DI" + element.getsimplename ()). Addmodifiers (Mo Difier. Public, modifier.final). Addmethod (Bindviewmethodspecbuilder.build ()). build (); Javafile javafile = Javafile.builder (Getpackagename (typeelement), TypeSpec). Build (); try {Javafile.writeto (Processingenv.getfiler ()); } catch (IOException e) {e.printstacktrace (); }} return true; } Private String GetpAckagename (typeelement type) {return elementutils.getpackageof (type). Getqualifiedname (). toString (); } @Override public synchronized void init (Processingenvironment processingenv) {super.init (processingenv); Elementutils = Processingenv.getelementutils (); } @Override Public SourceVersion getsupportedsourceversion () {return sourceversion.release_7; }}
We've introduced two libraries here.
Compile ' com.google.auto.service:auto-service:1.0-rc2 ' compile ' com.squareup:javapoet:1.7.0 '
Let's create another Java module anotation
Visible, is two annotation classes:
@Target (Elementtype.type) @Retention (retentionpolicy.class) public @interface lactivity {}
@Target (Elementtype.field) @Retention (retentionpolicy.runtime) public @interface lview { int value () default 0;}
Then our main project introduced these two module can be used under our main project with this annotation, we make project will be in the project directory under BUILD/GENERATED/SOURCE/APT to generate the corresponding Java source file, For example, I use a defined annotation in the activity class below:
@LActivitypublic class Testprocessoractivity extends Activity { @LView (r.id.et_input) EditText Inputview; @LView (R.id.button) button buttonview; @Override protected void onCreate (@Nullable Bundle savedinstancestate) { super.oncreate (savedinstancestate) ; Setcontentview (r.layout.activity_processor); Ditestprocessoractivity.bindview (this); Buttonview.setonclicklistener (New View.onclicklistener () { @Override public void OnClick (View v) { Toast.maketext (Testprocessoractivity.this , Inputview.gettext (). toString (), Toast.length_short). Show (); }); }}
The Ditestprocessoractivity.java is generated under BUILD/GENERATED/SOURCE/APT
Public final class Ditestprocessoractivity {public static void BindView (testprocessoractivity activity) { Activity.inputview = (android.widget.EditText) Activity.findviewbyid (2131165237); Activity.buttonview = (Android.widget.Button) Activity.findviewbyid (2131165220); }}
The code has been automatically generated, we do not need to write Findviewbyid (), only need to call Ditestprocessoractivity BindView method is ok.
@LView (r.id.et_input) EditText Inputview; @LView (R.id.button) button buttonview;
Third, need to understand
Our above examples mainly use Javapoet and Auto-service, detailed use can refer to the source code Https://github.com/square/javapoet, and Autoservice is relatively simple, is in the use of Java When APT is used, the meta-information can be generated automatically using Autoservice annotations. There are many related articles on the Internet, can be well organized under the study.
Android Custom processor for BindView functionality