安卓AOP三剑客之Android APT技术浅谈

移动开发 Android
APT(Annotation Processing Tool 的简称),可以在代码编译期解析注解,并且生成新的 Java 文件,减少手动的代码输入。现在有很多主流库都用上了 APT,比如 Dagger2, ButterKnife, EventBus3 等。

通过学习与使用square公司的开源项目javapoet,来实现仓库层动态生成代码

Android APT技术浅谈

安卓AOP三剑客: APT, AspectJ, Javassist

Android APT技术浅谈

Android APT

APT(Annotation Processing Tool 的简称),可以在代码编译期解析注解,并且生成新的 Java 文件,减少手动的代码输入。现在有很多主流库都用上了 APT,比如 Dagger2, ButterKnife, EventBus3 等

代表框架:

  • DataBinding
  • Dagger2
  • ButterKnife
  • EventBus3
  • DBFlow
  • AndroidAnnotation

使用姿势

1,在android工程中,创建一个java的Module,写一个类继承AbstractProcessor

 

@AutoService(Processor.class) // javax.annotation.processing.IProcessor 
@SupportedSourceVersion(SourceVersion.RELEASE_7) //java 
@SupportedAnnotationTypes({ // 标注注解处理器支持的注解类型 
    "com.annotation.SingleDelegate"
    "com.annotation.MultiDelegate" 
}) 
public class AnnotationProcessor extends AbstractProcessor { 
 
public static final String PACKAGE = "com.poet.delegate"
public static final String CLASS_DESC = "From poet compiler"
 
public Filer filer; //文件相关的辅助类 
public Elements elements; //元素相关的辅助类 
public Messager messager; //日志相关的辅助类 
public Types types; 
 
@Override 
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { 
    filer = processingEnv.getFiler(); 
    elements = processingEnv.getElementUtils(); 
    messager = processingEnv.getMessager(); 
    types = processingEnv.getTypeUtils(); 
 
    new SingleDelegateProcessor().process(set, roundEnvironment, this); 
    new MultiDelegateProcessor().process(set, roundEnvironment, this); 
 
    return true


  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.

2,在继承AbstractProcessor类中的process方法,处理我们自定义的注解,生成代码:

 

public class SingleDelegateProcessor implements IProcessor {  
@Override 
public void process(Set<? extends TypeElement> set, RoundEnvironment roundEnv, 
                AnnotationProcessor abstractProcessor) { 
// 查询注解是否存在 
Set<? extends Element> elementSet = 
        roundEnv.getElementsAnnotatedWith(SingleDelegate.class); 
Set<TypeElement> typeElementSet = ElementFilter.typesIn(elementSet); 
if (typeElementSet == null || typeElementSet.isEmpty()) { 
    return
 
// 循环处理注解 
for (TypeElement typeElement : typeElementSet) { 
    if (!(typeElement.getKind() == ElementKind.INTERFACE)) { // 只处理接口类型 
        continue
    } 
 
    // 处理 SingleDelegate,只处理 annotation.classNameImpl() 不为空的注解 
    SingleDelegate annotation = typeElement.getAnnotation(SingleDelegate.class); 
    if ("".equals(annotation.classNameImpl())) { 
        continue
    } 
    Delegate delegate = annotation.delegate(); 
 
    // 添加构造器 
    MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder() 
            .addModifiers(Modifier.PUBLIC); 
 
    // 创建类名相关 class builder 
    TypeSpec.Builder builder = 
            ProcessUtils.createTypeSpecBuilder(typeElement, annotation.classNameImpl()); 
 
    // 处理 delegate 
    builder = ProcessUtils.processDelegate(typeElement, builder, 
            constructorBuilder, delegate); 
 
    // 检查是否继承其它接口 
    builder = processSuperSingleDelegate(abstractProcessor, builder, constructorBuilder, typeElement); 
 
    // 完成构造器 
    builder.addMethod(constructorBuilder.build()); 
 
    // 创建 JavaFile 
    JavaFile javaFile = JavaFile.builder(AnnotationProcessor.PACKAGE, builder.build()).build(); 
    try { 
        javaFile.writeTo(abstractProcessor.filer); 
    } catch (IOException e) { 
        e.printStackTrace(); 
    } 


  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.

3,在项目Gradle中添加 annotationProcessor project 引用

 

compile project(':apt-delegate-annotation' 
annotationProcessor project(':apt-delegate-compiler'
  • 1.
  • 2.

4,如果有自定义注解的话,创建一个java的Module,专门放入自定义注解。项目与apt Module都需引用自定义注解Module

4-1,主工程:

 

compile project(':apt-delegate-annotation' 
annotationProcessor project(':apt-delegate-compiler'
  • 1.
  • 2.

4-2,apt Module:

 

compile project(':apt-delegate-annotation' 
compile 'com.google.auto.service:auto-service:1.0-rc2' 
compile 'com.squareup:javapoet:1.4.0' 
  • 1.
  • 2.
  • 3.

5,生成的源代码在build/generated/source/apt下可以看到

Android APT技术浅谈

难点

就apt本身来说没有任何难点可言,难点一在于设计模式和解耦思想的灵活应用,二在与代码生成的繁琐,你可以手动字符串拼接,当然有更高级的玩法用squareup的javapoet,用建造者的模式构建出任何你想要的源代码

优点

它的强大之处无需多言,看代表框架的源码,你可以学到很多新姿势。总的一句话:它可以做任何你不想做的繁杂的工作,它可以帮你写任何你不想重复代码。懒人福利,老司机必备神技,可以提高车速,让你以任何姿势漂移。它可以生成任何源代码供你在任何地方使用,就像剑客的剑,快疾如风,无所不及

我想稍微研究一下,APT还可以在哪些地方使用,比如:Repository层?

APT在Repository层的尝试

了解APT与简单学习之后,搭建Repository层时,发现有一些简单,重复模版的代码

每一次添加新接口都需要简单地修改很多地方,能不能把一部分代码自动生成,减少改动的次数呢?

Repository层

Android APT技术浅谈

IRemoteDataSource, RemoteDataSourceImpl

远程数据源,属于网络请求相关

ILocalDataSource, LocalDataSourceImpl

本地数据源,属于本地数据持久化相关

IRepository,RepositoryImpl

仓库代理类,代理远程数据源与本地数据源

Repository层APT设计思路

发现在具体实现类中,大多都是以代理类的形式调用:方法中调用代理对象,方法名称与参数,返回值类型都相同。显然可以进行APT的尝试

  • 简单的情况,具体实现类中只有一个代理对象
  • 复杂的情况,有多个代理对象,方法内并有一些变化

期望结果:

  • 把RemoteDataSourceImpl自动化生成
  • 把LocalDataSourceImpl自动化生成
  • 把RepositoryImpl自动化生成

自定义注解设计

要想具体实现类自动生成,首先要知道需要什么:

  • 方便自动生成java文件的类库
  • 自动生成类名字是什么
  • 需要注入的代理对象
  • 让代理对象代理的方法集

自动生成java文件的类库,可以使用 squareup javapoet

自动生成类名字,代理对象,方法集需要通过自定义注解配置参数的形成,在AbstractProcessor中获取

Delegate

 

@Retention(RetentionPolicy.SOURCE) 
@Target(ElementType.TYPE) 
public @interface Delegate { 
 
/** 
 * delegate class package 
 */ 
String delegatePackage(); 
 
/** 
 * delegate class name 
 */ 
String delegateClassName(); 
 
/** 
 * delegate simple name 
 */ 
String delegateSimpleName(); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

SingleDelegate

 

@Retention(RetentionPolicy.SOURCE) 
@Target(ElementType.TYPE) 
public @interface SingleDelegate { 
 
/** 
 * impl class name 
 */ 
String classNameImpl(); 
 
/** 
 * delegate data 
 */ 
Delegate delegate(); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

MultiDelegate

 

@Retention(RetentionPolicy.SOURCE) 
@Target(ElementType.TYPE) 
public @interface MultiDelegate {  
/** 
 * impl class name 
 */ 
String classNameImpl();  
/** 
 * delegate list 
 */ 
Delegate[] Delegates(); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

处理自定义的注解、生成代码

AnnotationProcessor

 

@AutoService(Processor.class) // javax.annotation.processing.IProcessor 
@SupportedSourceVersion(SourceVersion.RELEASE_7) //java 
@SupportedAnnotationTypes({ // 标注注解处理器支持的注解类型 
    "com.annotation.SingleDelegate"
    "com.annotation.MultiDelegate" 
}) 
public class AnnotationProcessor extends AbstractProcessor {  
public static final String PACKAGE = "com.poet.delegate"
public static final String CLASS_DESC = "From poet compiler" 
public Filer filer; //文件相关的辅助类 
public Elements elements; //元素相关的辅助类 
public Messager messager; //日志相关的辅助类 
public Types types;  
@Override 
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { 
    filer = processingEnv.getFiler(); 
    elements = processingEnv.getElementUtils(); 
    messager = processingEnv.getMessager(); 
    types = processingEnv.getTypeUtils(); 
 
    new SingleDelegateProcessor().process(set, roundEnvironment, this); 
    new MultiDelegateProcessor().process(set, roundEnvironment, this); 
 
    return true


  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
责任编辑:未丽燕 来源: 喜欢自己才会拥抱生活
相关推荐

2019-06-27 10:06:54

Linux 性能工具

2011-03-28 16:04:44

nagios

2023-10-04 00:20:31

grepLinux

2017-07-25 08:53:14

CorrectLinkCCA-SD算法

2021-05-13 10:25:29

Linuxgrep命令

2018-05-04 15:18:01

DockerDocker Comp容器

2021-03-15 07:39:48

LinuxAwk 语言

2010-02-04 16:22:21

2011-04-11 11:01:03

AndroidHTC苹果

2011-07-04 09:07:54

2009-02-26 18:22:49

桌面虚拟化Linux

2024-06-04 00:20:00

Python函数

2019-08-20 14:29:45

grepsedawk

2014-11-26 10:18:32

Cloud Setupwindows在线打包工具

2011-08-06 23:58:34

爱普生投影机

2009-03-19 20:52:58

LinuxPHPCMS

2023-11-25 17:08:47

ChatbotLLAMALangChain

2021-02-21 08:19:55

面试StringStringBuffe

2009-02-12 09:12:27

JPAEJBJSF

2023-09-18 08:27:20

RabbitMQRocketMQKafka
点赞
收藏

51CTO技术栈公众号