什么是AOP?

AOP面向切面编程,就是在代码预编译阶段,在不修改源代码的情况下,给程序添加某一功能。

像成熟的框架,ARouter,ButterKnife等也都使用了这个技术。任何技术的出现都有其实际应用场景,为了解决某一方面的痛点。AOP的出现让某些功能组件的封装更加解耦,使用者能够更加的方便的使用组件里的功能。

拿ButterKnife举例,我们原生开发,以前经常写很多findViewById的代码,显然这类代码写起来很繁琐,且容易出错(id和view有时候没对上)。而AOP可以有效避免这些问题。

比如我们可以通过在预编译的阶段解析注解,然后生成对应的java文件,该java文件封装了findviewbyid的方法,实现view和id的动态绑定。这样就非常有效减少了后期的编写代码的工作量,可以快速实现view和id的绑定操作。

AOP编程思路:

一.定义注解:自定义注解参考下面例子即可,default表示该字段不是必传字段,下面例子的自定义注解在使用的时候必须传入name。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Laucher {String name();boolean async() default false;int priority() default -1;String[] preTasks() default {};
}
元注解     说明
@Target     定义该自定义注解类型。
@Retention     这个注解的的存活时间
@Document     表明注解可以被javadoc此类的工具文档化
@Inherited     是否允许子类继承该注解,默认为false@Target   类型     说明
ElementType.TYPE     接口、类、枚举、注解
ElementType.FIELD     字段、枚举的常量
ElementType.METHOD     方法
ElementType.PARAMETER     方法参数
ElementType.CONSTRUCTOR     构造函数
ElementType.LOCAL_VARIABLE     局部变量
ElementType.ANNOTATION_TYPE     注解
ElementType.PACKAGE     包  @Retention                  说明
RetentionPolicy.SOURCE     注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃
RetentionPolicy.CLASS     注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期
RetentionPolicy.RUNTIME     注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在

二.编译阶段解析注解:创建一个java-library(不是androd library),实现自定义Processor,注册Processor。引入auto-service可以实现自动注册。rebuild之后,在build目录会生成javax.annotation.processing.Processor文件,这个文件能看到你注册的Processor。

dependencies {implementation project(path: ':xlauncher-annotation')implementation 'com.squareup:javapoet:1.8.0'annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7'compileOnly 'com.google.auto.service:auto-service-annotations:1.0-rc7'
}
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({"com.azx.xlauncher_annotation.Laucher"})
@AutoService(Processor.class)
public class LauncherProcessor extends AbstractProcessor {private Map<String, LauncherTask> taskMap = new HashMap<>();private String inputParamsName = "taskMap";private Filer filer;private Writer docWriter;private Logger logger;Elements elementUtils;@Overridepublic synchronized void init(ProcessingEnvironment processingEnv) {super.init(processingEnv);logger = new Logger(processingEnv.getMessager());filer = processingEnv.getFiler();elementUtils = processingEnv.getElementUtils();try {docWriter = filer.createResource(StandardLocation.SOURCE_OUTPUT,PACKAGE_OF_GENERATE_DOCS,PACKAGE_OF_GENERATE_DOCS+".json").openWriter();} catch (IOException e) {logger.error(e);}}@Overridepublic boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {Set<? extends Element> launcherElements = roundEnvironment.getElementsAnnotatedWith(Laucher.class);parseElements(launcherElements);try {createJavaDoc(launcherElements);} catch (IOException e) {e.printStackTrace();}return true;}private void createJavaDoc(Set<? extends Element> elements) throws IOException {ParameterizedTypeName mapTaskLauncher = ParameterizedTypeName.get(ClassName.get(HashMap.class),ClassName.get(String.class),ClassName.get(LauncherTask.class));ParameterSpec paramSpec = ParameterSpec.builder(mapTaskLauncher, inputParamsName).build();MethodSpec.Builder method = MethodSpec.methodBuilder(METHOD_NAME).addAnnotation(Override.class).addModifiers(PUBLIC).addParameter(paramSpec);addCodeIntoMethod(method, elements);JavaFile.builder(PACKAGE_OF_GENERATE_FILE,TypeSpec.classBuilder(CLASS_NAME)  //class文件名字.addModifiers(Modifier.PUBLIC)  //公有方法.addSuperinterface(ClassName.get(elementUtils.getTypeElement(ITASK_MANAGER))).addJavadoc("@Launcher注解自动生成的java文件").addMethod(method.build()).build()).build().writeTo(filer);}private void addCodeIntoMethod(MethodSpec.Builder method, Set<? extends Element> elements) {ClassName classTask = ClassName.get(LauncherTask.class);Set<String> keys = taskMap.keySet();for (Element item : elements) {Laucher annotation = item.getAnnotation(Laucher.class);String key = annotation.name();logger.info(key + "  " + ClassName.get((TypeElement)item));method.addStatement("$T.build($S, $T.class, " + taskMap.get(key).getPriority() + "," + taskMap.get(key).isAsync() + ", " + createListMethod(taskMap.get(key).getPreTaskList()) + ")",classTask,taskMap.get(key).getTaskName(),ClassName.get((TypeElement)item));}}private String createListMethod(List<String> list) {List<String> test = new ArrayList() {{add("a");}};StringBuilder builder = new StringBuilder();for (String item : list) {builder.append("add(\"" + item + "\"); ");}return "new java.util.ArrayList<String>(){{" + builder.toString() + "}}";}private void parseElements(Set<? extends Element> elements) {taskMap.clear();for (Element item : elements) {LauncherTask launcherTask = new LauncherTask();Laucher annotation = item.getAnnotation(Laucher.class);launcherTask.setTaskName(annotation.name());launcherTask.setAsync(annotation.async());launcherTask.setPriority(annotation.priority());launcherTask.setPreTaskList(Arrays.asList(annotation.preTasks()));taskMap.put(launcherTask.getTaskName(), launcherTask);}}
}

三.生成java文件:

我们可以看上面的LauncherProcessor类的createJavaCode的方法,里面的代码是整个生成java文件的逻辑。

我们先通过parseElements方法获取使用到Laucher注解的Elements,然后解析Laucher注解内部的参数。

接下来我们一步步阅读代码

ParameterizedTypeName mapTaskLauncher = ParameterizedTypeName.get(ClassName.get(HashMap.class),ClassName.get(String.class),ClassName.get(LauncherTask.class));  
//这个代码实际上就是创建一个参数类型,对应的代码是HashMap<String, LauncherTask>
private String inputParamsName = "taskMap";
ParameterSpec paramSpec = ParameterSpec.builder(mapTaskLauncher, inputParamsName).build();
//这个代码就是生成一个参数,对应的java代码是
HashMap<String, LauncherTask> taskMap

//下面的代码是构建一个方法对象,public属性,入参是HashMap<String, LauncherTask> taskMap,带有Override注解

MethodSpec.Builder method = MethodSpec.methodBuilder(METHOD_NAME).addAnnotation(Override.class).addModifiers(PUBLIC).addParameter(paramSpec);addCodeIntoMethod(method, elements);
private void addCodeIntoMethod(MethodSpec.Builder method, Set<? extends Element> elements) {ClassName classTask = ClassName.get(LauncherTask.class);Set<String> keys = taskMap.keySet();for (Element item : elements) {Laucher annotation = item.getAnnotation(Laucher.class);String key = annotation.name();logger.info(key + "  " + ClassName.get((TypeElement)item));method.addStatement("$T.build($S, $T.class, " + taskMap.get(key).getPriority() + "," + taskMap.get(key).isAsync() + ", " + createListMethod(taskMap.get(key).getPreTaskList()) + ")",classTask,taskMap.get(key).getTaskName(),ClassName.get((TypeElement)item));}}

//在方法体里面插入代码。

JavaFile.builder(PACKAGE_OF_GENERATE_FILE,TypeSpec.classBuilder(CLASS_NAME)  //class文件名字.addModifiers(Modifier.PUBLIC)  //公有方法.addSuperinterface(ClassName.get(elementUtils.getTypeElement(ITASK_MANAGER))).addJavadoc("@Launcher注解自动生成的java文件").addMethod(method.build()).build()).build().writeTo(filer);

构建class,并把方法插入,通过javadoc接口加入类的注解,并设置class实现的接口等,然后写入文件。

经过上面代码的处理,我们可以在build目录下面找到生成后的java文件,下图就是生成代码的结果。

四.可能遇到的问题:

1.Processor,init方法没走!

可能原因1:配置问题,jdk没统一,如果设置的jdk-8,结果annotation和compile的lbrary设置了java-7,其次也有可能是androd studio的jdk设置不是jdk-8。

可能原因2:SupportedAnnotationTypes设置错误,在log里面警告没找到对应注解。

2.Processor,init方法走了,但是process方法不执行。

1.java定义的注解用在了kotlin文件上面

2.某个模块使用了该注解,也implement了annotation的lib,但是没导入有自定义Processor的compile的lib。

Android AOP 编程实践 javapoet + autoService相关推荐

  1. Android AOP编程(五)——Gradle插件+TransformAPI+字节码插桩实战

    开篇 在前面几篇博文中,我记录了Android AOP编程的一些基础知识,包括Gradle插件的开发.TransformAPI的使用,以及一些操作字节码的工具如AspectJ,Javassist和AS ...

  2. Android切面编程(AOP)详解

    文章目录 什么是AOP AOP使用场景 AspectJ AspectJ基本概念 AspectJ的使用 引入AspectJ 创建切面AspectJ 添加注解 什么是AOP AOP是Aspect Orie ...

  3. Android AOP 面向切面编程

    一.AOP即面向切面编程 AOP 是 Aspect Oriented Programming 的缩写,译为面向切面编程.用我们最常用的 OOP 来对比理解: 纵向关系 OOP,横向角度 AOP 举个例 ...

  4. Android——面向AOP编程(二) Xerath 开源AOP框架

    承接上文Android--面向AOP编程(一)https://pumpkin.blog.csdn.net/article/details/119849325https://pumpkin.blog.c ...

  5. Android AOP之字节码插桩

    背景   本篇文章基于<网易乐得无埋点数据收集SDK>总结而成,关于网易乐得无埋点数据采集SDK的功能介绍以及技术总结后续会有文章进行阐述,本篇单讲SDK中用到的Android端AOP的实 ...

  6. 【编程实践】Google Guava 极简教程

    前言 Guava 工程包含了若干被 Google 的 Java 项目广泛依赖 的核心库,我们希望通过此文档为 Guava 中最流行和最强大的功能,提供更具可读性和解释性的说明. 适用人群 本教程是基础 ...

  7. Java-gt;Android并发编程引气入门篇

    Android的并发编程,即多线程开发,而Android的多线程开发模型也是源于Java中的多线程模型. 所以本篇也会先讲一些Java中的多线程理念,再讲解具体涉及的类,最后深入Android中的并发 ...

  8. Android Aop预研

    预研目的 公司要求,希望我整理一下项目中的log日志,由于当前项目已经很庞大,包含多个自主开发的library,并且由多个开发人员共同完成.不同的同事,打log的方式都不一样,没有同一个的格式,因此我 ...

  9. Android高级编程.

    Android高级编程. 2011新版Ja va教程.ppt: http://www.t00y.com/file/60254549 andbook英文版本.pdf: http://www.t00y.c ...

最新文章

  1. java解数独_java解数独
  2. python访问数据库日志文件_python利用inotify实现把nginx日志实时写入数据库
  3. 为CentOS 6 配置本地YUM源
  4. UVa 1354 天平难题 枚举二叉树
  5. sql服务器默认密码_搭建一个DNS服务器,轻松实现域名解析内容分发,访问速度提高N倍...
  6. pca百分比取多少比较好_母亲节给妈妈发多少红包比较好合适 母亲节红包吉利数字含义...
  7. UTF-8 's format
  8. Centos 7安装报错:Warning:dracut-initqueue timeout - starting timeout scripts
  9. java关键字值transient
  10. XAMPP 使用教程
  11. amd服务器开启虚拟化技术,在AMD平台上实现KVM虚拟化技术
  12. 高一计算机算法教案,教科版 高一信息技术 必修1 第四单元 4.2 数值计算 教案...
  13. java wsimport https_java – 当服务器需要客户端证书时如何使用wsimport?
  14. 学习markdown
  15. 洛谷 P1338 末日的传说
  16. linux下git和github搭建使用教程
  17. Excel if函数用法
  18. 数据库“交叉表”查询的实现
  19. 什么是依赖注入和控制反转
  20. Java第三天笔记01——流程控制语句

热门文章

  1. 智能世界2030八大展望
  2. jQuery是什么?和它的优缺点
  3. 潜匿的怪物,你的供应链真的安全吗?
  4. TM1638驱动数码管的一点建议,附程序
  5. 计算机二级公共基础知识点
  6. Android 验证码输入框的实现
  7. 怎么把html换成gif,怎么将mp4转换成gif 把mp4转换成高清gif的方法
  8. stm32串口通信最后一字节不对异常
  9. Linux环境:Zookeeper:Error contacting service. It is probably not running
  10. 从STM32F407到AT32F407(一)