当前位置:   article > 正文

插件化组件化 : APT : APT原理分析_apt和插件化

apt和插件化

https://blog.csdn.net/cpcpcp123/category_9620735.html

Q:APT原理是什么,怎么被执行起来的?

A:在javac编译时,先通过SPI机制加载所有的APT实现类,并将解析到需要编译的java源文件中所有的注解信息,与APT声明的支持处理的注解信息进行匹配,若待编译的源文件中存在APT声明的注解,则调起APT实现类的process方法。


Q:APT中process方法到底执行几次?为什么这么设计?

A:最少执行两次,最多无数次。最后一次执行可以视为Finish结束通知,执行收尾工作。


Q:APT中process方法boolean返回值返回true或者false有什么影响?

A: true:声明的注解只能被当前APT处理;false:在当前APT处理完声明的注解后,仍能被其他APT处理

1:不同module 之间Activity跳转处理器

1.1:定义注解

  1. 针对BBS和shop以及主module各模块之间跳转,我们定义四个注解
  2. @Target(ElementType.TYPE)
  3. @Retention(RetentionPolicy.CLASS)
  4. public @interface AutoRouter {
  5. }
  6. @Target(ElementType.TYPE)
  7. @Retention(RetentionPolicy.CLASS)
  8. public @interface Component {
  9. String value();
  10. }
  11. @Retention(RetentionPolicy.CLASS)
  12. @Target(ElementType.TYPE)
  13. public @interface Components {
  14. String[] value();
  15. }
  16. @Target(ElementType.TYPE)
  17. @Retention(RetentionPolicy.CLASS)
  18. public @interface StaticRouter {
  19. String value();
  20. }

1.2:编码处理器RouterProcessor

  1. @AutoService(Processor.class)
  2. public class RouterProcessor extends AbstractProcessor {
  3. private Filer mFiler;
  4. private Messager mMessager;
  5. private Map<String, String> mStaticRouterMap = new HashMap<>();
  6. private List<String> mAutoRouterList = new ArrayList<>();
  7. @Override
  8. public synchronized void init(ProcessingEnvironment processingEnvironment) {
  9. super.init(processingEnvironment);
  10. mFiler = processingEnvironment.getFiler();
  11. mMessager = processingEnvironment.getMessager();
  12. }
  13. @Override
  14. public SourceVersion getSupportedSourceVersion() {
  15. return SourceVersion.latestSupported();
  16. }
  17. @Override
  18. public Set<String> getSupportedAnnotationTypes() {
  19. Set<String> set = new HashSet<>();
  20. set.add(AutoRouter.class.getCanonicalName());
  21. set.add(StaticRouter.class.getCanonicalName());
  22. set.add(Component.class.getCanonicalName());
  23. set.add(Components.class.getCanonicalName());
  24. return set;
  25. }
  26. @Override
  27. public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
  28. mStaticRouterMap.clear();
  29. mAutoRouterList.clear();
  30. try {
  31. Set<? extends Element> mainAppElement = roundEnvironment.getElementsAnnotatedWith(Components.class);
  32. if (!mainAppElement.isEmpty()) {
  33. processInstaller(mainAppElement);
  34. return true;// 第一轮执行APT (创建 APP module对应的 注解类)
  35. }
  36. // 第二轮或者第三轮执行APT (创建 bbslib 和 shoplib module对应的 注解类)
  37. processComponent(roundEnvironment);
  38. } catch (Exception e) {
  39. mMessager.printMessage(Diagnostic.Kind.ERROR, e.getMessage());
  40. }
  41. return true;
  42. }
  43. private void processInstaller(Set<? extends Element> mainAppElement) throws IOException {
  44. TypeElement typeElement = (TypeElement) mainAppElement.iterator().next();
  45. JavaFileObject javaFileObject = mFiler.createSourceFile(Config.ROUTER_MANAGER, typeElement); // RouterManager
  46. PrintWriter writer = new PrintWriter(javaFileObject.openWriter());
  47. writer.println("package " + Config.PACKAGE_NAME + ";"); // org.loader.router
  48. writer.println("import org.loader.router.Router_shop;");// 导包 router_shop
  49. writer.println("import org.loader.router.Router_bbs;");// 导包 Router_bbs
  50. writer.println("public class " + Config.ROUTER_MANAGER + " {"); // RouterManager
  51. writer.println("public static void " + Config.ROUTER_MANAGER_METHOD + "() {");// setup
  52. Components componentsAnnotation = typeElement.getAnnotation(Components.class);
  53. String[] components = componentsAnnotation.value();
  54. for (String item : components) {
  55. writer.println(Config.FILE_PREFIX + item + ".router();"); //Router_
  56. }
  57. writer.println("}");
  58. writer.println("}");
  59. writer.flush();
  60. writer.close();
  61. }
  62. private void processComponent(RoundEnvironment roundEnvironment) throws Exception {
  63. Set<? extends Element> compElements = roundEnvironment.getElementsAnnotatedWith(Component.class);
  64. if (compElements.isEmpty()) { return;}
  65. Element item = compElements.iterator().next();
  66. String componentName = item.getAnnotation(Component.class).value();
  67. Set<? extends Element> routerElements = roundEnvironment.getElementsAnnotatedWith(StaticRouter.class);
  68. for (Element e : routerElements) {
  69. if (! (e instanceof TypeElement)) { continue;}
  70. TypeElement typeElement = (TypeElement) e;
  71. String pattern = typeElement.getAnnotation(StaticRouter.class).value();
  72. mStaticRouterMap.put(pattern, typeElement.getQualifiedName().toString());
  73. }
  74. Set<? extends Element> autoRouterElements = roundEnvironment.getElementsAnnotatedWith(AutoRouter.class);
  75. for (Element e : autoRouterElements) {
  76. if (!(e instanceof TypeElement)) { continue;}
  77. TypeElement typeElement = (TypeElement) e;
  78. mAutoRouterList.add(typeElement.getQualifiedName().toString());
  79. }
  80. writeComponentFile(componentName);
  81. }
  82. private void writeComponentFile(String componentName) throws Exception {
  83. String className = Config.FILE_PREFIX + componentName;// Router_
  84. JavaFileObject javaFileObject = mFiler.createSourceFile(className);
  85. // javaFileObject.delete();
  86. PrintWriter printWriter = new PrintWriter(javaFileObject.openWriter());
  87. printWriter.println("package " + Config.PACKAGE_NAME + ";");
  88. printWriter.println("import android.app.Activity;");
  89. printWriter.println("import android.app.Service;");
  90. printWriter.println("import android.content.BroadcastReceiver;");
  91. printWriter.println("public class " + className + " {");
  92. printWriter.println("public static void router() {");
  93. // // Router.router(ActivityRule.ACTIVITY_SCHEME + "shop.main", ShopActivity.class);
  94. for(Map.Entry<String, String> entry : mStaticRouterMap.entrySet()) {
  95. printWriter.println("org.loader.router.Router.router(\"" + entry.getKey()
  96. +"\", "+entry.getValue()+".class);");
  97. }
  98. for (String klass : mAutoRouterList) {
  99. printWriter.println("if (Activity.class.isAssignableFrom(" + klass + ".class)) {");
  100. printWriter.println("org.loader.router.Router.router(org.loader.router.rule.ActivityRule.ACTIVITY_SCHEME + \""
  101. +klass+"\", " + klass + ".class);");
  102. printWriter.println("}");
  103. printWriter.println("else if (Service.class.isAssignableFrom(" + klass + ".class)) {");
  104. printWriter.println("org.loader.router.Router.router(org.loader.router.rule.ServiceRule.SERVICE_SCHEME + \""
  105. +klass+"\", " + klass + ".class);");
  106. printWriter.println("}");
  107. printWriter.println("else if (BroadcastReceiver.class.isAssignableFrom(" + klass + ".class)) {");
  108. printWriter.println("org.loader.router.Router.router(org.loader.router.rule.ReceiverRule.RECEIVER_SCHEME + \""
  109. +klass+"\", "+klass+".class);");
  110. printWriter.println("}");
  111. }
  112. printWriter.println("}");
  113. printWriter.println("}");
  114. printWriter.flush();
  115. printWriter.close();
  116. }

1.3: 生成APT源码

1.3.1 在APP主module中生成 RouterManager.java源码

  1. package org.loader.router;
  2. import org.loader.router.Router_shop;
  3. import org.loader.router.Router_bbs;
  4. public class RouterManager {
  5. public static void setup() {
  6. Router_shop.router();
  7. Router_bbs.router();
  8. }
  9. }

1.3.2:在Shop module中,生成Router_shop.java源码

  1. package org.loader.router;
  2. import android.app.Activity;
  3. import android.app.Service;
  4. import android.content.BroadcastReceiver;
  5. public class Router_shop {
  6. public static void router() {
  7. org.loader.router.Router.router("activity://shop.main", org.loader.shoplib.ShopActivity.class);
  8. }
  9. }

1.3.3:在bbs module中,生成Router_bbs.java源码

  1. package org.loader.router;
  2. import android.app.Activity;
  3. import android.app.Service;
  4. import android.content.BroadcastReceiver;
  5. public class Router_bbs {
  6. public static void router() {
  7. if (Activity.class.isAssignableFrom(org.loader.bbslib.BBSActivity.class)) {
  8. org.loader.router.Router.router(org.loader.router.rule.ActivityRule.ACTIVITY_SCHEME + "org.loader.bbslib.BBSActivity", org.loader.bbslib.BBSActivity.class);
  9. }
  10. else if (Service.class.isAssignableFrom(org.loader.bbslib.BBSActivity.class)) {
  11. org.loader.router.Router.router(org.loader.router.rule.ServiceRule.SERVICE_SCHEME + "org.loader.bbslib.BBSActivity", org.loader.bbslib.BBSActivity.class);
  12. }
  13. else if (BroadcastReceiver.class.isAssignableFrom(org.loader.bbslib.BBSActivity.class)) {
  14. org.loader.router.Router.router(org.loader.router.rule.ReceiverRule.RECEIVER_SCHEME + "org.loader.bbslib.BBSActivity", org.loader.bbslib.BBSActivity.class);
  15. }
  16. }
  17. }

2:仿ButterKnife给Activity各控件绑定Id

2.1 : 定义注解

  1. // 保留到编译阶段,不加载进JVM
  2. @Retention(RetentionPolicy.CLASS)
  3. // 作用对象
  4. @Target(ElementType.FIELD)
  5. public @interface BindView {
  6. int value(); // 属性名为value的int 变量
  7. }

2.2 :编码注解处理器

  1. @SuppressWarnings("unused")
  2. @AutoService(Processor.class)
  3. public class BindViewProcessor extends AbstractProcessor {
  4. // Element代表程序的元素,例如包、类、方法。
  5. private Elements elementUtils;
  6. @Override
  7. public synchronized void init(ProcessingEnvironment processingEnvironment) {
  8. super.init(processingEnvironment);
  9. elementUtils = processingEnvironment.getElementUtils();
  10. }
  11. /**
  12. * @return 指定java版本。
  13. */
  14. @Override
  15. public SourceVersion getSupportedSourceVersion() {
  16. return SourceVersion.latestSupported();
  17. }
  18. /**
  19. * 指定该目标的注解对象,指定注解处理器是注册给哪个注解的,返回指定支持的注解类集合。
  20. *
  21. * @return Set<String> getCanonicalName即包名.类名,不同的对象获取的值不同,可能为空
  22. */
  23. @Override
  24. public Set<String> getSupportedAnnotationTypes() {
  25. Set<String> hashSet = new HashSet<>();
  26. hashSet.add(BindView.class.getCanonicalName());
  27. return hashSet;
  28. }
  29. /**
  30. * 处理包含指定注解对象的代码元素
  31. * 获取控件变量的引用以及对应的viewId,先遍历出每个Activity所包含的所有注解对象
  32. *
  33. * @param set Set<? extends TypeElement>
  34. * @param roundEnvironment RoundEnvironment 所有注解的集合
  35. * @return true
  36. */
  37. @Override
  38. public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
  39. // 获取所有包含BindView注解的元素
  40. Set<? extends Element> elementSet = roundEnvironment.getElementsAnnotatedWith(BindView.class);
  41. // 此处的TypeElement就是Activity
  42. // Activity中包含的 id以及对应的属性(控件)
  43. Map<TypeElement, Map<Integer, VariableElement>> typeElementMapHashMap = new HashMap<>();
  44. for (Element element : elementSet) {
  45. // 注解的是FIELD,因此可以直接转换
  46. VariableElement variableElement = (VariableElement) element;
  47. // 获取最里层的元素,此处就是Activity
  48. TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();
  49. // 获取对应Activity中的Map viewId View
  50. Map<Integer, VariableElement> variableElementMap = typeElementMapHashMap.get(typeElement);
  51. if (variableElementMap == null) {
  52. variableElementMap = new HashMap<>();
  53. typeElementMapHashMap.put(typeElement, variableElementMap);
  54. }
  55. // 获取注解对象
  56. BindView bindView = variableElement.getAnnotation(BindView.class);
  57. // 获取注解值
  58. int id = bindView.value();
  59. variableElementMap.put(id, variableElement);
  60. }
  61. for (TypeElement key : typeElementMapHashMap.keySet()) {
  62. Map<Integer, VariableElement> elementMap = typeElementMapHashMap.get(key);
  63. String packageName = elementUtils.getPackageOf(key).getQualifiedName().toString();
  64. JavaFile javaFile = JavaFile.builder(packageName, generateCodeByPoet(key,
  65. elementMap)).build();
  66. try {
  67. javaFile.writeTo(processingEnv.getFiler());
  68. } catch (IOException e) {
  69. e.printStackTrace();
  70. }
  71. }
  72. return true;
  73. }
  74. private TypeSpec generateCodeByPoet(TypeElement typeElement, Map<Integer, VariableElement> variableElementMap) {
  75. //自动生成的文件以 Activity名 + ViewBinding 进行命名
  76. return TypeSpec.classBuilder(typeElement.getSimpleName().toString() + "ViewBinding")
  77. .addModifiers(Modifier.PUBLIC)
  78. .addMethod(generateMethodByPoet(typeElement, variableElementMap))
  79. .build();
  80. }
  81. /**
  82. * @param typeElement 注解对象的根元素,即Activity
  83. * @param variableElementMap Activity包含的注解对象以及注解的目标对象
  84. * @return MethodSpec
  85. */
  86. private MethodSpec generateMethodByPoet(TypeElement typeElement, Map<Integer, VariableElement> variableElementMap) {
  87. ClassName className = ClassName.bestGuess(typeElement.getQualifiedName().toString());
  88. // _mainActivity.btn_serializeSingle = (android.widget.Button) (_mainActivity.findViewById(2131165221));
  89. // 第一个转小写+下划线
  90. String parameter = "_" + Utils.toLowerCaseFirstChar(className.simpleName());
  91. MethodSpec.Builder builder = MethodSpec.methodBuilder("bind") // 方法名
  92. .addModifiers(Modifier.PUBLIC, Modifier.STATIC) // public static
  93. .returns(void.class)// 返回类型
  94. .addParameter(className, parameter);
  95. for (int viewId : variableElementMap.keySet()) {
  96. VariableElement variableElement = variableElementMap.get(viewId);
  97. // 变量名
  98. String fieldName = variableElement.getSimpleName().toString();
  99. // 变量父类的全称
  100. String fieldType = variableElement.asType().toString();
  101. String text = "{0}.{1} = ({2})({3}.findViewById({4}));";
  102. builder.addCode(MessageFormat.format(text, parameter, fieldName, fieldType, parameter, String.valueOf(viewId)));
  103. }
  104. return builder.build();
  105. }
  106. }

2.3 生成注解文件

  1. package org.loader.module2module;
  2. public class MainActivityViewBinding {
  3. public static void bind(MainActivity _mainActivity) {
  4. _mainActivity.tv = (android.widget.TextView)(_mainActivity.findViewById(2131165313));}
  5. }

3: 项目工程代码结构

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/240089
推荐阅读
相关标签
  

闽ICP备14008679号