introduce
APT(Annotation Processing Tool), an annotation processor, is a tool for processing annotations, specifically a tool for javac, which scans and processes annotations at compile time. The annotation processor takes Java code (or compiled bytecode) as input and generates. java files as output.
Simply put, at compile time, annotations are used to generate. java files.
Advantage
The advantage of using APT is that it is convenient, simple and can save a lot of repetitive code.
Students who have used ButterKnife, Dagger, EventBus and other annotation frameworks can feel that using these frameworks can save a lot of code, just write some annotations.
In fact, they just generated some code through annotations. Through learning APT, you will find that they are very strong.~~~
Using steps
1. apt-annotation
@Retention(RetentionPolicy.CLASS) //Runtime annotations
@Target(ElementType.FIELD) //Represents the scope of annotations for class members (constructors, methods, member variables)
public @interface AttachView {
int value();
}
2. apt-processor (Annotation Processor)
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.google.auto.service:auto-service:1.0-rc2'//Google helps us quickly implement annotation processors
compile project(':apt-annotation')//java lib for self-defined annotations
compile 'com.squareup:javapoet:1.7.0'//Used to generate java files to avoid the embarrassment of string splicing
}
Create AttachProcessor
@AutoService(Processor.class)
public class ActivityInjectProcesser extends AbstractProcessor {
private Filer mFiler; //File-related ancillary classes
private Elements mElementUtils; //Element-related auxiliary classes Many elements
private Messager mMessager; //Log-related ancillary classes
private Map<String, AnnotatedClass> mAnnotatedClassMap;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
mFiler = processingEnv.getFiler();
mElementUtils = processingEnv.getElementUtils();
mMessager = processingEnv.getMessager();
mAnnotatedClassMap = new TreeMap<>();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
mAnnotatedClassMap.clear();
try {
processActivityCheck(roundEnv);
} catch (Exception e) {
e.printStackTrace();
error(e.getMessage());
}
for (AnnotatedClass annotatedClass : mAnnotatedClassMap.values()) {
try {
annotatedClass.generateActivityFile().writeTo(mFiler);
} catch (Exception e) {
error("Generate file failed, reason: %s", e.getMessage());
}
}
return true;
}
private void processActivityCheck(RoundEnvironment roundEnv) throws IllegalArgumentException, ClassNotFoundException {
//check ruleslass forName(String className
for (Element element : roundEnv.getElementsAnnotatedWith((Class<? extends Annotation>) Class.forName(TypeUtil.ANNOTATION_PATH))) {
if (element.getKind() == ElementKind.CLASS) {
getAnnotatedClass(element);
} else
error("ActivityInject only can use in ElementKind.CLASS");
}
}
private AnnotatedClass getAnnotatedClass(Element element) {
// tipe . can not use chines so ....
// get TypeElement element is class's --->class TypeElement typeElement = (TypeElement) element
// get TypeElement element is method's ---> TypeElement typeElement = (TypeElement) element.getEnclosingElement();
TypeElement typeElement = (TypeElement) element;
String fullName = typeElement.getQualifiedName().toString();
AnnotatedClass annotatedClass = mAnnotatedClassMap.get(fullName);
if (annotatedClass == null) {
annotatedClass = new AnnotatedClass(typeElement, mElementUtils, mMessager);
mAnnotatedClassMap.put(fullName, annotatedClass);
}
return annotatedClass;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new LinkedHashSet<>();
types.add(TypeUtil.ANNOTATION_PATH);
return types;
}
private void error(String msg, Object... args) {
mMessager.printMessage(Diagnostic.Kind.ERROR, String.format(msg, args));
}
private void log(String msg, Object... args) {
mMessager.printMessage(Diagnostic.Kind.NOTE, String.format(msg, args));
}
}
public class AnnotatedClass {
private TypeElement mTypeElement;//activity //fragmemt
private Elements mElements;
private Messager mMessager;//Log printing
public AnnotatedClass(TypeElement typeElement, Elements elements, Messager messager) {
mTypeElement = typeElement;
mElements = elements;
this.mMessager = messager;
}
public JavaFile generateActivityFile() {
// build inject method
MethodSpec.Builder injectMethod = MethodSpec.methodBuilder(TypeUtil.METHOD_NAME)
.addModifiers(Modifier.PUBLIC)
.addParameter(TypeName.get(mTypeElement.asType()), "activity", Modifier.FINAL);
injectMethod.addStatement("android.widget.Toast.makeText" +
"(activity, $S,android.widget.Toast.LENGTH_SHORT).show();", "from build");
//generaClass
TypeSpec injectClass = TypeSpec.classBuilder(mTypeElement.getSimpleName() + "$$InjectActivity")
.addModifiers(Modifier.PUBLIC)
.addMethod(injectMethod.build())
.build();
String packgeName = mElements.getPackageOf(mTypeElement).getQualifiedName().toString();
return JavaFile.builder(packgeName, injectClass).build();
}
}
public class TypeUtil {
public static final String METHOD_NAME = "inject";
public static final String ANNOTATION_PATH = "com.example.apt_annotation.AttachView";
}
3. apt-library tool class
public class InjectActivity {
private static final ArrayMap<String, Object> injectMap = new ArrayMap<>();
public static void inject(AppCompatActivity activity) {
String className = activity.getClass().getName();
try {
Object inject = injectMap.get(className);
if (inject == null) {
Class<?> aClass = Class.forName(className + "$$InjectActivity");
inject = aClass.newInstance();
injectMap.put(className, inject);
}
Method m1 = inject.getClass().getDeclaredMethod("inject", activity.getClass());
m1.invoke(inject, activity);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Using the tool class, reflection calls the inject method toast of the following class
public class MainActivity$$InjectActivity {
public void inject(final MainActivity activity) {
android.widget.Toast.makeText(activity, "from build",android.widget.Toast.LENGTH_SHORT).show();;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file_list);
InjectActivity.inject(this);//Call the class generated by build
ยทยทยท
Annotation implements the unified Java Toast method as above.