Android 通过 APT 解耦模块依赖

写在前面

Android APT 的新玩法,生成类的特殊加载方式。在 Android 多 module 工程中使用 APT,会出现类冲突问题,如果你也碰上这种问题,希望本文对你有所帮助。

对本文有任何问题,可加我的个人微信:kymjs123

APT 是什么?Annotation Process Tool,注解处理工具。
这本是 Java 的一个工具,但 Android 也可以使用,他可以用来处理编译过程时的某些操作,比如 Java 文件的生成,注解的获取等。

在 Android 上,我们使用 APT 通常是为了生成某些处理标注有指定注解的方法、类或变量,比如 EventBus3.0开始,就是使用 APT 去处理onEvent 注解的;dagger2、butterknife 等著名的开源库也都是使用 APT 去实现的。再举一个大家非常熟悉的实际使用场景:在 Android 模块化重构的过程中,就会需要大量用到 APT 去生成作为跨模块转发层的中间类,在我之前讲《饿了么模块化平台设计》中的铁金库 IronBank 就大量使用了 APT 与 AOP 技术去实现跨模块的处理工作。

实现 APT

当然,本文要讲的是 APT 的新玩法,讲 APT demo 的文章有太多了,大家随便网上搜一下就一大把,如果会了的同学,可以跳过本节。
要实现一个简单的 APT demo 是很容易的。首先在 idea 中创建一个 Java 工程(由于 Android Studio 不能直接创建 Java 工程,我们选用 idea 更简单)

1、首先创建一个我们需要处理的注解声明:

@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.METHOD})
public @interface Produce {

    Class<?> returnType() default Produce.class;

    Class<?>[] params() default {};
}

关于注解类的创建以及上面各个给注解类加注解的含义,在我很早之前的一篇博客《Android注解式绑定控件,没你想象的那么难》中已经有很详细的介绍了,不知道的同学可以再去看一看。

2、第二步,我们为了之后处理方便,创建一个 JavaBean 用来封装需要的数据。

class ItemData {
    Element element;
    String className = "";
    String returnType = "";
    String methodName = "";
    String[] params = {};
}

3、最后就是最重要的一个类了:注解是处理方式

public class MyAnnotationProcessor extends AbstractProcessor {
}

所有的注解处理类必须继承自系统的AbstractProcessor,如果想要让这个注解处理类生效,还要在我们的工程中创建一个 meta 文件,meta 文件中写好要提供注解处理功能的那个类的包名+类名。比如我的是这样写的:

开源实验室

3.1、重写两个方法

public class MyAnnotationProcessor extends AbstractProcessor {

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> supportTypes = new HashSet<>();
        supportTypes.add(Produce.class.getCanonicalName());
        return supportTypes;
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        boolean isProcess = false;
        try {
            isProcess = true;
            List<ItemData> creatorList = parseProduce(roundEnvironment);
            genJavaFile(creatorList);
        } catch (Exception e) {
            isProcess = false;
        }
        return isProcess;
    }
}

getSupportedAnnotationTypes是用来告诉 APT,我要关注的注解类型是哪些类型。这里只有一个注解@Produce所以我们的 set 就只添加了一个类型。
process()就是真正用于处理注解的函数,这里我是通过parseProduce()返回了所有被@Produce修饰的方法的信息,就是我们前面封装的 JavaBean,包含了方法所在类名、方法返回值、方法名、方法参数等信息。
然后再通过genJavaFile()去生成方法对应的跨模块的中间类。

top Created with Sketch.