引言
在Android组件化开发中,路由表的自动生成是解耦组件导航的核心。本文将通过注解处理工具(APT)和Javapoet,实现一个类型安全、多模块支持的路由处理器,并深入解析@AutoService
的关键作用。
一、核心架构与目标
1. 系统架构
-
注解:
@Route
标记路由类 - 处理器:扫描注解生成路由表
-
服务加载:通过
ServiceLoader
动态获取路由表
2. 核心功能
功能 | 技术实现 | 优势 |
---|---|---|
路由标注 | @Route(path = "/main/home") |
清晰声明路由路径 |
自动生成路由表 | Javapoet生成Java类 | 避免手动维护映射关系 |
多模块支持 | 通过moduleName 参数区分 |
独立生成各模块路由表 |
自动注册 |
@AutoService +SPI |
无需手动配置服务文件 |
二、核心实现详解
1. 注解处理器初始化
@AutoService(Processor.class) // 注册处理器自身
public class RouteProcessor extends AbstractProcessor {
private Filer filer;
private Elements elementUtils;
private String moduleName; // 从gradle获取的模块名
@Override
public void init(ProcessingEnvironment env) {
super.init(env);
filer = env.getFiler();
elementUtils = env.getElementUtils();
moduleName = env.getOptions().get("moduleName");
}
}
2. 处理@Route
注解
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Map<String, ClassName> routeMap = new HashMap<>();
String packageName = null;
// 收集所有被@Route注解的类
for (Element element : roundEnv.getElementsAnnotatedWith(Route.class)) {
TypeElement typeElement = (TypeElement) element;
if (packageName == null) {
packageName = elementUtils.getPackageOf(typeElement).getQualifiedName().toString();
}
Route route = typeElement.getAnnotation(Route.class);
routeMap.put(route.path(), ClassName.get(typeElement));
}
generateRouteClass(routeMap, packageName); // 生成路由表
return true;
}
3. 生成路由表类(关键代码)
private void generateRouteClass(Map<String, ClassName> routeMap, String packageName) {
// 定义Map类型:Map<String, Class<?>>
TypeName mapType = ParameterizedTypeName.get(
Map.class, String.class, ParameterizedTypeName.get(Class.class, WildcardTypeName.subtypeOf(Object.class))
);
// 构建getRouteMap方法
MethodSpec method = MethodSpec.methodBuilder("getRouteMap")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.returns(mapType)
.addStatement("$T<$T, $T> map = new $T<>()", Map.class, String.class, Class.class, HashMap.class);
// 填充路由数据
routeMap.forEach((path, clazz) ->
method.addStatement("map.put($S, $T.class)", path, clazz)
);
method.addStatement("return map");
// 生成类并添加@AutoService注解
TypeSpec routeClass = TypeSpec.classBuilder(moduleName + "$RouteTable")
.addAnnotation(AnnotationSpec.builder(AutoService.class)
.addMember("value", "$T.class", RouteTable.class) // 关键注解
.build())
.addSuperinterface(RouteTable.class)
.addMethod(method)
.build();
JavaFile.builder(packageName, routeClass).build().writeTo(filer);
}
三、@AutoService的核心作用
1. SPI服务加载机制
传统方式需要手动创建META-INF/services/接口全限定名
文件:
# 内容为实现类全限定名
com.github.xrouter.main$RouteTable
@AutoService优势:
- 编译期自动生成服务配置文件
- 避免多模块手动配置错误
- 与
ServiceLoader
无缝集成
2. 注解原理
// 生成的路由表类
@AutoService(RouteTable.class) // 声明实现RouteTable接口
public final class main$RouteTable implements RouteTable {
@Override public Map<String, Class<?>> getRouteMap() { /*...*/ }
}
执行流程:
- AutoService处理器扫描到
@AutoService
- 在
build/generated
目录生成服务配置文件 - 运行时通过
ServiceLoader.load(RouteTable.class)
自动发现
四、使用指南
1. Gradle配置
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [
"moduleName": project.getName() // 传递模块名
]
}
}
}
}
dependencies {
annotationProcessor "com.google.auto.service:auto-service:1.0-rc7"
implementation project(':core') // 包含@Route和RouteTable的库
}
2. 标记路由
@Route(path = "/user/profile")
public class ProfileActivity extends Activity {
// ...
}
3. 生成结果
// 生成的路由表类(示例)
@AutoService(RouteTable.class)
public final class user$RouteTable implements RouteTable {
@Override
public Map<String, Class<?>> getRouteMap() {
Map<String, Class<?>> map = new HashMap<>();
map.put("/user/profile", ProfileActivity.class);
return map;
}
}
4. 使用路由表
// 加载所有模块的路由表
ServiceLoader.load(RouteTable.class).forEach(table -> {
table.getRouteMap().forEach((path, clazz) -> {
Log.d("ROUTER", "Path: " + path + ", Class: " + clazz.getName());
});
});
五、设计亮点
1. 类型安全
- 使用
Class<?>
泛型约束 - 编译期检查路由路径重复
2. 多模块隔离
// 模块名作为类名后缀
moduleName + "$RouteTable" // 如main$RouteTable、order$RouteTable
3. 扩展性设计
- 支持路由分组:通过
@Route(group = "user")
- 可添加优先级、拦截器等元数据
六、常见问题
1. 模块名未配置
if (moduleName == null) {
processingEnv.getMessager().printMessage(
Diagnostic.Kind.ERROR,
"请在gradle中配置moduleName参数"
);
}
2. 包名不一致
处理器自动以第一个被注解类的包名作为生成类包名,支持多模块不同包结构。
七、总结与扩展
核心价值
维度 | 传统方式 | 本文方案 |
---|---|---|
维护成本 | 手动维护路由映射 | 完全自动化 |
类型安全 | 运行时ClassNotFoundException | 编译期检查 |
多模块支持 | 复杂的跨模块配置 | 自动生成独立路由表 |
扩展方向
- 支持路由参数解析(如
@RouteParam
) - 集成路由跳转逻辑(替代显式Intent)
- 添加单元测试验证生成结果