一、Annotation Processor简介
Annotation Processor表示注解处理器;在java代码的编译阶段,Annotation Processor会对注解进行处理,在这个机制上,我们可以自定义Annotation Processor来实现对注解的不同处理。
在Dagger、butterknife等开源库中都使用到了Annotation Processor。
二、背景描述
假设:需要创建一个路由管理类RouterManager()来实现对不同的scheme做不同的分发,即这个类可以接收一个string参数,然后根据这个string参数跳转不同的逻辑;我们可以怎么实现呢?
2.1 定义Router接口如下
public interface IRouter {
void dispatch();
}
2.2 实现IRouter
public class CodeRouter implements IRouter {
@Override
public void dispatch() {
Log.d(ROUTER_TAG, "CodeRouter");
}
}
或者:
public class HttpRouter implements IRouter {
@Override
public void dispatch() {
Log.d(ROUTER_TAG, "HttpRouter");
}
}
2.3 新建RouterManager类
public class RouterManager {
private HashMap<String, String> map = new HashMap<>();
private HashMap<String, IRouter> routerMap = new HashMap<>();
private static final class Host {
private static final RouterManager instance = new RouterManager();
}
private RouterManager() {
}
public static RouterManager getInstance() {
return Host.instance;
}
public void initRouter() {
RouterManager.getInstance().register("bc://code", "com.bc.router.CodeRouter");
RouterManager.getInstance().register("bc://http", "com.bc.router.HttpRouter");
}
public void register(String uri, String className) {
if (className != null && uri != null) {
map.put(uri, className);
routerMap.put(uri, null);
}
}
public void showAllScheme() {
System.out.println("RouterManager:" + map.toString());
}
public boolean dispatch(String scheme) {
try {
if (routerMap.containsKey(scheme)) {
IRouter router = routerMap.get(scheme);
if (router == null) {
router = (IRouter) Class.forName(map.get(scheme)).newInstance();
routerMap.put(scheme, router);
}
router.dispatch();
return true;
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return false;
}
}
2.4 初始化及使用
class Activity{
public void onCreate() {
RouterManager.getInstance().initRouter();
}
public void onClick() {
RouterManager.getInstance().dispatch("bc://code");
}
}
这种实现方式的缺点是:每次有新增的Router时都需要在RouterManager.initRouter中调用一次注册register方法。在有多个模块时这种方式不够灵活,下面改用注解+Annotation Processor的实现方法。
三、Annotation Processor的实现方法
(1)新建Java Library模块用于存放annotation以及基础RouterManager。
(2)新建Java Library模块lib_compiler用于AnnotationProcessor注解处理过程。
3.1 自定义注解RouterProvider
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface RouterProvider {
public String uri() default "";
}
3.2 在IRouter实现类上添加注解
@RouterProvider(uri = "bc://code")
public class CodeRouter implements IRouter {
@Override
public void dispatch() {
Log.d(ROUTER_TAG, "CodeRouter");
}
}
或者
@RouterProvider(uri = "bc://http")
public class HttpRouter implements IRouter {
@Override
public void dispatch() {
Log.d(ROUTER_TAG, "HttpRouter");
}
}
3.3 新建RouterManager
与之前实现唯一不同地方在于initRouter(),这里调用了一个TestRouterInit.initRouter()方法来实现初始化,TestRouterInit及其initRouter()方法都是在后面的Annotation Processor过程中动态生成的,定义如下:
public class RouterManager {
public static final String INIT_CLASS = "com.bc.router.TestRouterInit";
public static final String INIT_PACKAGE = "com.bc.router";
public static final String INIT_SIMPLE_CLASS = "TestRouterInit";
public static final String INIT_METHOD = "initRouter";
public static final String ROUTER_TAG = "router";
private HashMap<String, String> map = new HashMap<>();
private HashMap<String, IRouter> routerMap = new HashMap<>();
private static final class Host {
private static final RouterManager instance = new RouterManager();
}
private RouterManager() {
}
public static RouterManager getInstance() {
return Host.instance;
}
public void initRouter() {
try {
Class.forName(INIT_CLASS).getMethod(INIT_METHOD).invoke(null);
} catch (Exception e) {
e.printStackTrace();
}
}
public void register(String uri, String className) {
if (className != null && uri != null) {
map.put(uri, className);
routerMap.put(uri, null);
}
}
public void showAllScheme() {
System.out.println("RouterManager:" + map.toString());
}
public boolean dispatch(String scheme) {
try {
if (routerMap.containsKey(scheme)) {
IRouter router = routerMap.get(scheme);
if (router == null) {
router = (IRouter) Class.forName(map.get(scheme)).newInstance();
routerMap.put(scheme, router);
}
router.dispatch();
return true;
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return false;
}
}
3.4 新建Annotation Processor模块
新建Annotation Processor模块lib_compiler,该模块需要依赖auto-service、javapoet,其build.gradle文件如下:
apply plugin: 'java-library'
apply plugin: 'kotlin'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// auto-service
compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
// javapoet
implementation 'com.squareup:javapoet:1.11.1'
// 这个模块存放着我们需要处理的注解
implementation project(path: ":annotation")
}
其中:
(1)auto-service:AutoService是Google开源的用来方便生成符合ServiceLoader规范的开源库
(2)Javapoet:JavaPoet是square推出的开源java代码生成框架,提供Java Api生成.java源文件。
3.5 自定义的Annotation Processor
继承AbstractProcessor来自定义Annotation Processor,并处理注解:
@AutoService(Processor.class)
public class TestRouterProcessor extends AbstractProcessor {
public static final String ROOT_INIT = RouterManager.INIT_PACKAGE;
public static final String INIT_CLASS = RouterManager.INIT_SIMPLE_CLASS;
public static final String INIT_METHOD = RouterManager.INIT_METHOD;
@Override public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
}
@Override public Set<String> getSupportedAnnotationTypes() {
return Collections.singleton(RouterProvider.class.getCanonicalName());
}
@Override public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
if (annotations == null || annotations.isEmpty()) {
return false;
}
try {
MethodSpec.Builder mainMethodBuilder = MethodSpec.methodBuilder(INIT_METHOD)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class);
for (Element elementItem : env.getElementsAnnotatedWith(RouterProvider.class)) {
if (!(elementItem instanceof TypeElement)) {
continue;
}
TypeElement element = (TypeElement) elementItem;
String className = element.getQualifiedName().toString();
String uri = element.getAnnotation(RouterProvider.class).uri();
}
TypeSpec testRouterInit = TypeSpec.classBuilder(INIT_CLASS)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(mainMethodBuilder.build())
.build();
JavaFile javaFile = JavaFile.builder(ROOT_INIT, testRouterInit)
.build();
Filer filer = processingEnv.getFiler();
javaFile.writeTo(filer);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
}
以上自定义的注解处理过程,最终生成了一个类TestRouterInit:
public class TestRouterInit() {
public static void initRouter() {
RouterManager.getInstance().register("bc://code", "com.bc.router.CodeRouter");
RouterManager.getInstance().register("bc://http", "com.bc.router.HttpRouter");
}
}
3.6 依赖自定义的Annotation Processor
annotationProcessor project(":lib_compiler")
在模块下添加了annotationProcessor后,自定义的AnnotationProcessor就会在编译过程中对注解进行处理。
3.7 初始化及使用
同上;
class Activity{
public void onCreate() {
RouterManager.getInstance().initRouter();
}
public void onClick() {
RouterManager.getInstance().dispatch("bc://code");
}
}
|