What is APT

The command-line utility apt, annotation processing tool, finds and executes annotation processors based on the annotations present in the set of specified source files being examined. The annotation processors use a set of reflective APIs and supporting infrastructure to perform their processing of program annotations (JSR 175). The apt reflective APIs provide a build-time, source-based, read-only view of program structure. These reflective APIs are designed to cleanly model the JavaTM programming language’s type system after the addition of generics (JSR 14). First, apt runs annotation processors that can produce new source code and other files. Next, apt can cause compilation of both original and generated source files, thus easing the development cycle.

APT(Annotation Processing Tool) is a javAC Tool for compiling annotations.

  1. Can be used to scan and process annotations at compile time
  2. Automatically generate code based on requirements by retrieving information about annotations and annotated objects

Ii. Basic Use (excluding Auto-service)

1. Add a Java Module: libannotation to the Android project to define annotations

Java @Retention(retentionPolicy.class) @target (elementType.field) public @interface BindView {int viewId() default 0; } // BindActivity.java @Retention(RetentionPolicy.CLASS) @Target(ElementType.TYPE) public @interface BindActivity { }Copy the code

2. Add another Java Module to the Android project: Libapt, which is used to write registered custom annotation handlers (i.e. handle BindView and BindActivity annotations).

2.1 We need to reference a third-party library in this Module: com. squareUp: Javapoet (Javapoet is an open source Java code generation framework launched by Square, providing Java Api generation. Java source files)

Add references to build.gradle because we need to handle custom annotations, Need to reference in dependencies ligannotation {/ / JavaPoet implementation 'com. Squareup: JavaPoet: 1.13.0' implementation project(":libannotation") }Copy the code

2.2 Define an annotation processor by inheriting AbstractProcessor

You can see that several core methods are provided

  • Init: ProcessingEnviroment provides a number of useful utility classes Elements, Types, and Filer, which are initialized by passing in processingEnv
  • GetSupportedSourceVersion: specify the use of Java version
  • GetSupportedAnnotationTypes: name which use annotations, the annotation processor provides here to fill out our custom annotations
  • Process: This is where annotations are scanned and processed
public class ViewProcessor extends AbstractProcessor { private Elements elementUtils; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); elementUtils = processingEnv.getElementUtils(); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public Set<String> getSupportedAnnotationTypes() { Set<String> types = new LinkedHashSet<>(); types.add(BindActivity.class.getCanonicalName()); types.add(BindView.class.getCanonicalName()); return types; } @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindActivity.class); for (Element element : elements) { TypeElement typeElement = (TypeElement) element; List<? extends Element> members = elementUtils.getAllMembers(typeElement); MethodSpec.Builder bindViewMethodSpecBuilder = MethodSpec.methodBuilder("bindActivity") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(TypeName.VOID) .addParameter(ClassName.get(typeElement.asType()), "currentActivity"); for (Element item : members) { BindView bindView = item.getAnnotation(BindView.class); if (bindView == null) { continue; } bindViewMethodSpecBuilder.addStatement(String.format("currentActivity.%s = (%s) currentActivity.findViewById(%s)", item.getSimpleName(), ClassName.get(item.asType()).toString(), bindView.viewId())); } TypeSpec typeSpec = TypeSpec.classBuilder("Bind" + element.getSimpleName()) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(bindViewMethodSpecBuilder.build()) .build(); JavaFile javaFile = JavaFile.builder(elementUtils.getPackageOf(typeElement).getQualifiedName().toString(), typeSpec).build(); try { javaFile.writeTo(processingEnv.getFiler()); } catch (IOException e) { e.printStackTrace(); } } return true; }}Copy the code

2.3 Specify the annotation handler (without this step, there will be no output)

  • Create a new resources folder under the main directory of the current Module
  • Create a new meta-INF /services folder under resources
  • In the meta-inf/services directory folder to create javax.mail. Annotation. Processing. The Processor file
  • In javax.mail. The annotation. Processing. Write the full name of annotation Processor Processor file, including the package path

3. Use in main Module: app

3.1 Reference libapt library and libannotaion library

dependencies {
    ....
    implementation project(":libannotation")
    annotationProcessor project(":libapt")
}
Copy the code

3.2 Use annotations in MainActivity

@BindActivity public class MainActivity extends AppCompatActivity { @BindView(viewId = R.id.text) TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }}Copy the code

3.3 rebuild project

In app\build\generated\ap_generated_sources\debug\ (Using old gradle may generate different folders: # the build/generated/source/apt/debug) or become a BindMainActivity, format of the Activity generated is according to the definition we are in the process of Processor format

3.4 Using the Activity

@BindActivity
public class MainActivity extends AppCompatActivity {

    @BindView(viewId = R.id.text)
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        BindMainActivity.bindActivity(this);
        textView.setText("change the text success!");
    }
}
Copy the code

3.5 Project Address

Gitee: demo download address

Third, add

One thing to note here is that you can’t merge libannotation and libapt into the same module and then refer to them in the App Module. You’ll see that this doesn’t work

Another question worth thinking about here is, do I have to go through such tedious steps every time I specify a custom annotation handler? Create a series of folders and specify the full path of the annotation handler? Is there an easy way?

Iv. Use of auto-service

The answer is yes. Google’s AutoService can help us automatically complete the specified steps and generate the corresponding files. The official documentation

The demo implementation in this chapter is similar to the implementation in the previous article

1. Introduce AutoService in build.gradle of libapt

Dependencies {/ / AutoService related implementation 'com. Google. Auto. Services: auto - service: 1.0' annotationProcessor 'com. Google. Auto. Services: auto - service: 1.0' implementation 'com. Google. Auto: auto - common: 1.0' implementation 'com. Squareup: javapoet: 1.13.0 implementation project (path:' : libannotation ')}Copy the code

2. Add annotations to the custom annotation handler

@AutoService(Processor.class)
public class ViewProcessor extends AbstractProcessor {
    ...
}
Copy the code

3. Remove the previous manually created folder and file. Otherwise, an error message is displayed

Execution failed for task ':libapt:jar'.
> Entry META-INF/services/javax.annotation.processing.Processor is a duplicate but no duplicate handling strategy has been set.
Copy the code

4. A rebuild

5. Project address

Gitee: demo download address

Five, think

By following the above process, we can already dispense with a bunch of FindViewByIds to find controls. This is a very simple version, but ButterKnife is much more powerful: Click event handling, use in fragments, ViewHolder binding support, and more. Of course, once we understand these fundamentals, we can try to extend other features as well.