虽然自定义注解处理器是Java1.5发布的特性了,但是仍然十分强大,我们将学习什么是注解处理器以及如何使用注解处理器和代码生成库来一些好用的功能。
我相信每个使用过Java的人都知道,Java自身就有很多注解,比如@Override用来声明重写方法,@Singleton用来申明单例,Android SDK中也定义了一些注解,比如@NonNull, @StringRes, @IntRes等等。
如果使用过Dagger2, Lombok, DeepLinkDispatch 这些开源库的话,应该知道@Inject、@Component和@Module等注解,通过在一些类或者方法上加上这些注解就能实现一些非常神奇的功能。实际上之所以能实现这些功能,是因为这些第三方库定义了一些注解处理器,在编译时,这些注解处理器根据注解自动生成了一些实现功能的Java代码。
下面开始实现自定义注解处理器,主要功能是通过注解,实现路由跳转,示例项目分为以下三个部分:
在annotation模块中,新建文件src/main/java/com/example/annotation/Route.java,声明自定义注解
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface Route {
String name() default "/";
}
在annotation-processor模块中,修改build.gradle引入依赖
dependencies {
//用于生成Java代码
implementation 'com.squareup:javapoet:1.13.0'
implementation project(':annotation')
//用于简化配置Processor,非必需,它本身也是google开发的一个注解处理器,代码比较简单,可以查看源码
annotationProcessor 'com.google.auto.service:auto-service:1.0'
implementation 'com.google.auto.service:auto-service-annotations:1.0'
}
然后编写自定义注解处理器,创建文件src/main/java/com/example/annotation/processor/RouteProcessor.java
1@AutoService(Processor.class)
2public class RouteProcessor extends AbstractProcessor {
3
4 @Override
5 public Set<String> getSupportedAnnotationTypes() {
6 return Collections.singleton(Route.class.getCanonicalName());
7 }
8
9 @Override
10 public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
11 if (env.processingOver()) {
12 generateSource();
13 } else {
14 parseAnnotation(env);
15 }
16 return true;
17 }
18}
1/**
2* 解析{@link com.example.annotation.Route}注解,把被它标注过的类加入HashMap中
3*
4* @param env
5*/
6private void parseAnnotation(RoundEnvironment env) {
7 for (Element element : env.getElementsAnnotatedWith(Route.class)) {
8 if (element.getKind() != ElementKind.CLASS) {
9 return;
10 }
11 TypeElement typeElement = (TypeElement) element;
12 Route uri = element.getAnnotation(Route.class);
13 routes.put(uri.url(), typeElement);
14 }
15}
16
17/**
18* 根据HashMap生成Java代码
19*/
20private void generateSource() {
21 TypeSpec.Builder navigatorClass = TypeSpec.classBuilder("Routers")
22 .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
23 MethodSpec.Builder getMethod = MethodSpec.methodBuilder("get")
24 .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
25 .addParameter(ClassName.get(String.class), "url")
26 .returns(ClassName.get(Class.class));
27 List<Map.Entry<String, TypeElement>> entrySet = new ArrayList<>(routes.entrySet());
28 for (int i = 0; i < entrySet.size(); i++) {
29 Map.Entry<String, TypeElement> entry = entrySet.get(i);
30 ClassName activityClass = ClassName.get(entry.getValue());
31 if (i == 0) {
32 getMethod.beginControlFlow("if (url.equals($S))", entry.getKey());
33 } else {
34 getMethod.nextControlFlow("else if (url.equals($S))", entry.getKey());
35 }
36 getMethod.addStatement("return $T.class", activityClass);
37 if (i == entrySet.size() - 1) {
38 getMethod.endControlFlow();
39 }
40 }
41 getMethod.addStatement("return null");
42 navigatorClass.addMethod(getMethod.build());
43 try {
44 JavaFile.builder(PKG, navigatorClass.build()).build().writeTo(processingEnv.getFiler());
45 } catch (IOException e) {
46 e.printStackTrace();
47 }
48}
最终通过注解自动生成的代码如下:
public final class Routers {
public static Class get(String url) {
if (url.equals("/settings")) {
return SettingsActivity.class;
} else if (url.equals("/")) {
return MainActivity.class;
}
return null;
}
}
这里仅仅是完成了最简单的路由跳转功能,没有考虑传参,以及调用结果之类的逻辑,完整的示例项目代码如下
一些优秀的路由框架:WMRouter、ARouter、DeepLinkDispatch
注释文件 | 大小 | 修改时间 |
---|---|---|
annotation | 2022年02月15日 | |
annotation-processor | 2022年02月15日 | |
app | 2022年02月15日 | |
build.gradle | 431 B | 2022年02月15日 |
gradle/wrapper | 2022年02月14日 | |
gradle.properties | 1 kB | 2022年02月15日 |
gradlew | 6 kB | 2022年02月14日 |
gradlew.bat | 3 kB | 2022年02月14日 |
local.properties | 437 B | 2022年02月14日 |
settings.gradle | 388 B | 2022年02月15日 |