该系列介绍自定义注解,完成如下功能。
前提
在某些情况下,我们不得不为了某些情形而在代码层面妥协,写出了一坨又一坨的反人类代码。比如接手了一个古老的工程,A模块依赖B模块,但是现在的需求需要在B里面打开A页面,或者调用A的方法来做复杂的计算等等,于是就催生了这个注解。。。
声明注解
1 2 3 4 5 6 7 8
| @Target(ElementType.TYPE) @Retention(RetentionPolicy.CLASS)
public @interface RouterModule { String host();
String schema() default "App"; }
|
1 2 3 4 5
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.CLASS) public @interface RouterPath { String value(); }
|
处理注解逻辑
这里我们新建一个处理器,专门用来处理这种逻辑,也重新生成另外一个辅助类
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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
| @AutoService(Processor.class) public class RouterProcessor extends AbstractProcessor { private Elements elementUtils;
private Map<Element, TypeSpecWrapper> typeSpecWrapperMap = new HashMap<>();
@Override public Set<String> getSupportedAnnotationTypes() { Set<String> set = new LinkedHashSet<>(); set.add(RouterModule.class.getCanonicalName()); set.add(RouterPath.class.getCanonicalName());
return set; }
@Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.RELEASE_7; }
@Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); elementUtils = processingEnv.getElementUtils(); }
@Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
typeSpecWrapperMap.clear();
Set<? extends Element> routerModuleSet = roundEnv.getElementsAnnotatedWith(RouterModule.class);
for (Element element : routerModuleSet) { RouterModule routerModule = element.getAnnotation(RouterModule.class); TypeSpecWrapper typeSpecWrapper = typeSpecWrapperMap.get(element); if (typeSpecWrapper == null) { String packageName = elementUtils.getPackageOf(element).getQualifiedName().toString(); ClassName hashMapClassName = ClassName.bestGuess("java.util.HashMap"); ClassName methodClassName = ClassName.bestGuess("java.lang.reflect.Method"); ClassName stringClassName = ClassName.bestGuess("java.lang.String"); ParameterizedTypeName routerMapClassName = ParameterizedTypeName.get(hashMapClassName, stringClassName, methodClassName);
ClassName targetClassName = ClassName.get(packageName, element.getSimpleName().toString());
TypeSpec.Builder typeSpecBuilder = TypeSpec.classBuilder(routerModule.schema() + routerModule.host() + "$$Router") .addField(routerMapClassName, "routerMap", Modifier.PRIVATE) .addField(targetClassName, "target"); typeSpecWrapper = new TypeSpecWrapper(typeSpecBuilder, "com.huangyuanlove.router"); typeSpecWrapperMap.put(element, typeSpecWrapper);
MethodSpec.Builder constructorBuilder = typeSpecWrapper.getMethodBuilder(MethodSpec.constructorBuilder().toString()); if (constructorBuilder == null) { constructorBuilder = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC).addException(Exception.class); } constructorBuilder.addStatement("this.target = new $T()", targetClassName) .addStatement("this.routerMap = new $T()", routerMapClassName);
List<? extends Element> lists = element.getEnclosedElements(); for (Element element1 : lists) { ExecutableElement temp = (ExecutableElement) element1;
RouterPath routerPath = temp.getAnnotation(RouterPath.class); if (routerPath != null) { constructorBuilder.addStatement("this.routerMap.put($S,target.getClass().getMethod($S,$L))", routerPath.value(), element1.getSimpleName().toString(), paramsClassString(temp)); }
}
typeSpecWrapper.putMethodBuilder(constructorBuilder);
ClassName paramWrapperName = ClassName.bestGuess("com.huangyuanlove.view_inject_api.router.RouterParamWrapper"); ClassName routerDelegateName = ClassName.bestGuess("com.huangyuanlove.view_inject_api.router.RouterDelegate");
MethodSpec.Builder invokeBuilder = MethodSpec.methodBuilder("invoke") .addModifiers(Modifier.PUBLIC) .addException(Exception.class) .addParameter(String.class, "path") .addParameter(paramWrapperName, "paramWrapper") .addStatement("$T method = this.routerMap.get($L)", methodClassName, "path") .beginControlFlow("if(method == null)") .addStatement(" throw new Exception(\"can not find method which map \" +path)") .endControlFlow() .addStatement("return $T.invoke(method,target,paramWrapper)", routerDelegateName) .returns(Object.class);
typeSpecWrapper.putMethodBuilder(invokeBuilder);
}
}
for (Map.Entry<Element, TypeSpecWrapper> entry : typeSpecWrapperMap.entrySet()) {
entry.getValue().writeTo(processingEnv.getFiler()); }
return true; }
private String paramsClassString(ExecutableElement temp) {
if (temp == null) { return null; }
List<? extends VariableElement> parameters = temp.getParameters();
if (parameters == null || parameters.size() == 0) { return null; }
String[] result = new String[parameters.size()];
StringBuilder sb = new StringBuilder();
for (int i = 0; i < parameters.size(); i++) { result[i] = parameters.get(i).asType().toString() + ".class"; sb.append(parameters.get(i).asType().toString() + ".class"); sb.append(","); } return sb.deleteCharAt(sb.length() - 1).toString(); } }
|
主要就是收集注解信息,生成注解类,在调用对应注解路径时,调用api
模块中对应的代理类RouterDelegate
,最终由使用者调用Router
中的RouterBuilder
来调用对应路径注解的方法。
给使用者提供调用方法
在api
模块中,新建RouterParamWrapper
作为我们注解参数的包装类
1 2 3 4 5 6 7 8 9 10 11
| public class RouterParamWrapper {
private Object []paramArray;
public RouterParamWrapper(Object [] paramArray) { this.paramArray = paramArray; } public Object[] getParamArray() { return paramArray; } }
|
在api
模块中,新建RouterDelegate
作为我们注解的代理类,实际上也就是一个中间层
1 2 3 4 5 6
| public class RouterDelegate {
public static Object invoke(Method method, Object target, RouterParamWrapper paramWrapper) throws Exception{ return method.invoke(target,paramWrapper.getParamArray()); } }
|
然后提供给调用者一个调用方法
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 52 53 54 55 56 57 58 59
| public class Router {
public interface InvokeResultListener<T>{ void onError(Exception e); void onSuccess( T result); }
private static final String PACKAGE_NAME = "com.huangyuanlove.router";
public static RouterBuilder to(String path){ return new RouterBuilder(path); }
public static class RouterBuilder{ private String path; private Object[] paramArray;
private RouterBuilder(String path) { this.path = path; }
public RouterBuilder addParam(Object ... paramArray){ this. paramArray = paramArray; return this;
}
public void done(){ done(null); }
public void done(InvokeResultListener listener){
try { Uri uri = Uri.parse(path); String schema = uri.getScheme(); String host = uri.getHost(); String path = uri.getPath();
Class routerInject = Class.forName(PACKAGE_NAME +"." + schema + host +"$$Router"); Constructor constructor = routerInject.getConstructor(); constructor.setAccessible(true); RouterParamWrapper paramWrapper = new RouterParamWrapper(paramArray);
Method invokeMethod = routerInject.getMethod("invoke",String.class,RouterParamWrapper.class); invokeMethod.setAccessible(true); Object result = invokeMethod.invoke(constructor.newInstance(),path,paramWrapper); if(listener!=null){ listener.onSuccess(result); } }catch (Exception e){
if(listener!=null){ listener.onError(e); } } } } }
|
这样使用者调用Router.to(String path).addParam(Object ... param).done
就可以调用了。
使用
我们可以在一个模块中提供一个功能类,用来暴露出提供给其他模块使用的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @RouterModule(schema = "App",host = "main") public class MainProvider {
@RouterPath(value = "/toMain") public void startMain(Activity context, int id){ Intent intent = new Intent(context, MainActivity.class); intent.putExtra("id",id); context.startActivity(intent); }
@RouterPath(value = "/toMainWithResult") public void startMain(Activity context, String title,int requestCode){ Intent intent = new Intent(context, MainActivity.class); intent.putExtra("title",title); context.startActivity(intent); }
@RouterPath(value = "/getInt") public int getIntValue(String s){ return s.length(); } }
|
然后我们就可以在其他模块中使用Router
进行任意调用了,比如
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
| @ClickResponder(idStr = "toAppMainActivity") public void toAppMainActivity(View v){
Router.to("App://main/toMain").addParam(this,123).done(new Router.InvokeResultListener() { @Override public void onError(Exception e) { Toast.makeText(EXT_MainActivity.this,e.toString(),Toast.LENGTH_SHORT).show();
}
@Override public void onSuccess(Object o) {
} });
}
@ClickResponder(idStr = "invoke_main_method") public void invokeMainMethod(View v){
Router.to("App://main/getInt").addParam("12345678").done(new Router.InvokeResultListener<Integer>() { @Override public void onError(Exception e) { Toast.makeText(EXT_MainActivity.this,e.toString(),Toast.LENGTH_SHORT).show(); }
@Override public void onSuccess(Integer result) { Toast.makeText(EXT_MainActivity.this,result+"",Toast.LENGTH_SHORT).show();
} });
}
|
其实我也不想搞出来这种东西,但毕竟是时间紧、任务重,先这么用着吧。
结语
终于把注解这一块写完了,也算是对自己写注解的一个总结吧。
以上