Android—APT实践
APT可以根據注解,在編譯時生成代碼。
1.?? ?創建兩個java library
?
2.?? ?依賴
factory-compiler的依賴
dependencies {implementation 'com.google.auto.service:auto-service:1.0-rc4'implementation project(':annotation')implementation 'com.squareup:javapoet:1.10.0'annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4' }?app的
implementation project(':annotation') annotationProcessor project(':factory-compiler')3.?? ?創建注解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.CLASS) public @interface Factory {Class type();String id();}4.?? ?注解使用
在app中創建接口,還有對應實現類
public interface IShape {void show(); }@Factory(type = IShape.class, id = "Circle") public class Circle implements IShape {@Overridepublic void show() {System.out.println("Circle");} }@Factory(type = IShape.class, id = "Rectangle") public class Rectangle implements IShape {@Overridepublic void show() {System.out.println("Rectangle");} }在MainActivity中使用
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);IShapeFactory iShapeFactory = new IShapeFactory();IShape circle = iShapeFactory.create("Circle");TextView tv = findViewById(R.id.tv);tv.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {circle.show();}});}@Overrideprotected void onResume() {super.onResume();} }此時我們還沒有IShapeFactory這個類,需要用APT在編譯生成。
5.?? ?實現APT
首先我們創建兩個實體類
public class FactoryAnnotatedClass {private TypeElement typeElement;private String canonicalName;private String simpleTypeName;private final String id;public FactoryAnnotatedClass(TypeElement classElement) {this.typeElement = classElement;Factory annotation = classElement.getAnnotation(Factory.class);id = annotation.id();try { // 該類已經被編譯Class<?> clazz = annotation.type();canonicalName = clazz.getCanonicalName();simpleTypeName = clazz.getSimpleName();} catch (MirroredTypeException mte) {// 該類未被編譯DeclaredType classTypeMirror = (DeclaredType) mte.getTypeMirror();TypeElement classTypeElement = (TypeElement) classTypeMirror.asElement();canonicalName = classTypeElement.getQualifiedName().toString();simpleTypeName = classTypeElement.getSimpleName().toString();}}……get方法 } public class FactoryGroup {private static final String SUFFIX = "Factory";public final String qualifiedClassName;private final Map<String, FactoryAnnotatedClass> itemsMap = new LinkedHashMap<>();public FactoryGroup(String qualifiedClassName) {this.qualifiedClassName = qualifiedClassName;}public void add(FactoryAnnotatedClass toInsert) {itemsMap.put(toInsert.getId(), toInsert);}public void generateCode(Elements elementUtils, Filer filer) {TypeElement superClassName = elementUtils.getTypeElement(qualifiedClassName);String factoryClassName = superClassName.getSimpleName() + SUFFIX;PackageElement pkg = elementUtils.getPackageOf(superClassName);String packageName = pkg.isUnnamed() ? null : pkg.getQualifiedName().toString();MethodSpec.Builder method = MethodSpec.methodBuilder("create").addModifiers(Modifier.PUBLIC).addParameter(String.class, "id").returns(TypeName.get(superClassName.asType()));method.beginControlFlow("if (id == null)").addStatement("throw new IllegalArgumentException($S)", "id is null!").endControlFlow();for (FactoryAnnotatedClass item : itemsMap.values()) {method.beginControlFlow("if ($S.equals(id))", item.getId()).addStatement("return new $L()", item.getTypeElement().getQualifiedName().toString()).endControlFlow();System.out.println("QualifiedName "+item.getTypeElement().getQualifiedName().toString());}method.addStatement("throw new IllegalArgumentException($S + id)", "Unknown id = ");TypeSpec typeSpec = TypeSpec.classBuilder(factoryClassName).addModifiers(Modifier.PUBLIC).addMethod(method.build()).build();try {if (packageName != null) {JavaFile.builder(packageName, typeSpec).build().writeTo(filer);}} catch (IOException e) {e.printStackTrace();}} }最主要的就是generateCode方法,里面利用了JavaPoet生成代碼。
接下來創建一個類繼承AbstractProcessor,主要實現下面兩個方法。
@AutoService(Processor.class) public class FactoryProcessor extends AbstractProcessor{private final Map<String, FactoryGroup> factoryClasses = new LinkedHashMap<>();@Overridepublic Set<String> getSupportedAnnotationTypes() {Set<String> annotations = new LinkedHashSet<>();annotations.add(Factory.class.getCanonicalName());return annotations;}@Overridepublic boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {for (TypeElement element:set){// 通過RoundEnvironment獲取到所有被@Factory注解的對象Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(element);for (Element annotatedElement : elements) {TypeElement typeElement = (TypeElement) annotatedElement;FactoryAnnotatedClass annotatedClass = new FactoryAnnotatedClass(typeElement);FactoryGroup factoryGroup = factoryClasses.get(annotatedClass.getCanonicalName());if (factoryGroup == null) {String qualifiedGroupName = annotatedClass.getCanonicalName();factoryGroup = new FactoryGroup(qualifiedGroupName);factoryClasses.put(qualifiedGroupName, factoryGroup);}factoryGroup.add(annotatedClass);}for (FactoryGroup factoryGroup : factoryClasses.values()) {factoryGroup.generateCode(processingEnv.getElementUtils(), processingEnv.getFiler());}return true;}return false;} }process方法會被多次調用,方法參數中的set是我們注解類所對應的TypeElement對象,僅當set不為空時,我們才去操作。
?然后調用RoundEnvironment的getElementsAnnotatedWith方法獲取到使用了改注解的類的TypeElement對象(既Circle、Rectangle)。
然后我們把這兩個TypeElement對象封裝成FactoryAnnotatedClass對象,存放到factoryGroup中,簡單說factoryGroup就是用來存放使用了同一個注解類的對象,為了方便我們創建多個注解一起使用所以又創建了一個map對象(既factoryClasses)來存放factoryGroup,然后我們調用generateCode方法生成對應java文件。
?在generateCode方法中,我們通過獲取Circle、Rectangle類注解的id,來生成代碼,rebuild之后即可看到對應文件。
?
優點:
1、當我們需要再創建一個實現類時,只需要給實現類標上注解就可以,不想要去添加工廠類的代碼。如果是自己編寫的工廠類則需要再加一段代碼。
2、APT>反射,解析注解可以不需要用反射的方式,APT在調式期間就可以解析。
JavaPoet使用文檔: https://github.com/aheckl/javapoet
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的Android—APT实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android—RecyclerView
- 下一篇: 无病呻吟系列