对于注解( Annotation
),我们在项目中经常会使用到,所以我们有必要知道注解( Annotation
)是怎么工作的,是怎么帮我们把复杂的业务逻辑解耦的;怎么自定义自己的注解( Annotation
)
注解( Annotation
)有两种,一种是 运行时注解
, 一种是 编译时注解
,下面我们一一介绍
我们先来简单介绍一下元注解;元注解就是用来定义自定义注解的注解,我们常用的元注解就四个 @Target
、 @Documented
、 @Retention
、 @Inherited
@Target
: 用来指定注解的使用范围;比如类、方法、字段等
public enum ElementType { TYPE, // 类声明 FIELD, // 字段声明 METHOD, //方法声明 PARAMETER, //参数声明 CONSTRUCTOR, //构造函数声明 LOCAL_VARIABLE, // 局部变量声明 ANNOTATION_TYPE, // 注释类型声明 PACKAGE, // 包声明 TYPE_PARAMETER, // 类型参数声明(常用于泛型的类型参数进行注解) TYPE_USE; // 类型使用声明(常用于泛型的类型参数进行注解) private ElementType() { } } 复制代码
@Documented
: 用来指定被标注的注解会包含在javadoc中
@Retention
: 用来指定注解的生命周期,比如源码、class、运行时
public enum RetentionPolicy { SOURCE, // 源码 CLASS, // class RUNTIME; // 运行时 private RetentionPolicy() { } } 复制代码
@Inherited
: 指定子类可以继承父类的注解,只能是类上的注解,方法和字段的注解不能被继承
运行时注解是指程序在运行的过程中,通过反射去获取方法、属性等成员的注解信息,来实现一些业务逻辑
比如给属性自动赋值
定义 BindValue
如下:
@Documented @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface BindValue { public String value() default ""; } 复制代码
然后定义一个解析 BindValue
的处理类
public class BindValueProcessor { public static void bind(Object object) { Field[] fields = object.getClass().getDeclaredFields(); for (Field field : fields) { // 通过field.getAnnotation拿到BindValue注解信息 // method、class等都有getAnnotation方法获取注解信息 BindValue bindValue = field.getAnnotation(BindValue.class); try { if (bindValue != null) { // 给field 赋值 为 @BindValue 注解上的 value 值 // 如果field是private,则需要调用setAccessible // field.setAccessible(true); field.set(object, bindValue.value()); } } catch (IllegalAccessException e) { e.printStackTrace(); } } } } 复制代码
最后再使用 @BindValue
注解
public class AnnotationActivity extends AppCompatActivity { // 给name赋值为 张三 @BindValue("张三") public String name; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_annotation); // 调用此方法解析@BindValue注解 BindValueProcessor.bind(this); } } 复制代码
运行时注解其实比较简单,就是通过反射获取对应成员的注解信息,然后做相应处理
上面的demo比较简单,主要是为了讲解运行时注解的工作原理,然后怎么去方便的定义一个自己的运行时注解
下面我们来看看运行时注解 在 Android
中的一些案例
看源码得知 EventBus
的对注解的解析有两种方案,一种是编译时解析(生成索引类),一种是运行时解析,下面我们来看看 EventBus
的运行时解析注解的源码
先看看 findSubscriberMethods
的实现,这里面就有注解解析两种方案的实现分支
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); ... // ignoreGeneratedIndex 默认为false if (ignoreGeneratedIndex) { // 运行时解析注解(使用反射) subscriberMethods = findUsingReflection(subscriberClass); } else { // 使用编译时解析注解生成的索引(注意 需要手动开启索引) // 如果在findUsingInfo没有找到索引,则还是使用反射 subscriberMethods = findUsingInfo(subscriberClass); } ... } 复制代码
下面我们来看看 EventBus
反射的具体实现
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) { FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); // while循环--读取父类的@Subscribe注解的信息 while (findState.clazz != null) { findUsingReflectionInSingleClass(findState); findState.moveToSuperclass(); } return getMethodsAndRelease(findState); } // 这个方法就是真正使用反射去解析注解的方法了 private void findUsingReflectionInSingleClass(FindState findState) { Method[] methods; try { // This is faster than getMethods, especially when subscribers are fat classes like Activities methods = findState.clazz.getDeclaredMethods(); } catch (Throwable th) { // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149 methods = findState.clazz.getMethods(); findState.skipSuperClasses = true; } for (Method method : methods) { int modifiers = method.getModifiers(); if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length == 1) { Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); if (subscribeAnnotation != null) { Class<?> eventType = parameterTypes[0]; if (findState.checkAdd(method, eventType)) { ThreadMode threadMode = subscribeAnnotation.threadMode(); findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky())); } } } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { String methodName = method.getDeclaringClass().getName() + "." + method.getName(); throw new EventBusException("@Subscribe method " + methodName + "must have exactly 1 parameter but has " + parameterTypes.length); } } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { String methodName = method.getDeclaringClass().getName() + "." + method.getName(); throw new EventBusException(methodName + " is a illegal @Subscribe method: must be public, non-static, and non-abstract"); } } } 复制代码
其实上面的代码 跟 我一开始写的 demo 原理一样,只是 EventBus
考虑到一些复杂的业务场景,做了一些封装而已;比如将 method
和 @Subscribe
注解上的信息封装成 SubscriberMethod
对象并保存起来,最后在调用 EventBus.getInstance().post(Event)
时,找到对应的 SubscriberMethod
对象,然后根据注解上的 threadMode
信息等在调用 method.invoke()
方法时做线程切换等
我们再来看一个 Android
里非常常用的组件库 Lifecycle
, 它的 @OnLifecycleEvent
也是运行时注解
废话不多说,直接看源码
// 解析Class @OnLifecycleEvent注解,并封装成CallbackInfo对象 CallbackInfo getInfo(Class klass) { CallbackInfo existing = mCallbackMap.get(klass); if (existing != null) { return existing; } existing = createInfo(klass, null); return existing; } private CallbackInfo createInfo(Class klass, @Nullable Method[] declaredMethods) { Class superclass = klass.getSuperclass(); Map<MethodReference, Lifecycle.Event> handlerToEvent = new HashMap<>(); if (superclass != null) { // 使用递归--不断解析父类的@OnLifecycleEvent注解 CallbackInfo superInfo = getInfo(superclass); if (superInfo != null) { handlerToEvent.putAll(superInfo.mHandlerToEvent); } } // 遍历所有的接口,解析@OnLifecycleEvent注解 Class[] interfaces = klass.getInterfaces(); for (Class intrfc : interfaces) { for (Map.Entry<MethodReference, Lifecycle.Event> entry : getInfo( intrfc).mHandlerToEvent.entrySet()) { // 发现重复定义,会覆盖 verifyAndPutHandler(handlerToEvent, entry.getKey(), entry.getValue(), klass); } } // 真正使用反射--解析@OnLifecycleEvent注解 Method[] methods = declaredMethods != null ? declaredMethods : getDeclaredMethods(klass); boolean hasLifecycleMethods = false; for (Method method : methods) { OnLifecycleEvent annotation = method.getAnnotation(OnLifecycleEvent.class); if (annotation == null) { continue; } hasLifecycleMethods = true; Class<?>[] params = method.getParameterTypes(); int callType = CALL_TYPE_NO_ARG; if (params.length > 0) { callType = CALL_TYPE_PROVIDER; if (!params[0].isAssignableFrom(LifecycleOwner.class)) { throw new IllegalArgumentException( "invalid parameter type. Must be one and instanceof LifecycleOwner"); } } Lifecycle.Event event = annotation.value(); if (params.length > 1) { callType = CALL_TYPE_PROVIDER_WITH_EVENT; if (!params[1].isAssignableFrom(Lifecycle.Event.class)) { throw new IllegalArgumentException( "invalid parameter type. second arg must be an event"); } if (event != Lifecycle.Event.ON_ANY) { throw new IllegalArgumentException( "Second arg is supported only for ON_ANY value"); } } if (params.length > 2) { throw new IllegalArgumentException("cannot have more than 2 params"); } MethodReference methodReference = new MethodReference(callType, method); // 发现重复定义,会覆盖 verifyAndPutHandler(handlerToEvent, methodReference, event, klass); } CallbackInfo info = new CallbackInfo(handlerToEvent); mCallbackMap.put(klass, info); mHasLifecycleMethods.put(klass, hasLifecycleMethods); return info; } 复制代码
Lifecycle
其实就是将 class
的 @OnLifecycleEvent
注解信息封装一个 CallbackInfo
对象,然后再封装成一个 GenericLifecycleObserver
对象, 并缓存在 Lifecycling
的缓存中,然后在当生命周期发生变化的时候在 dispatchEvent
方法中调用 GenericLifecycleObserver
的 onStateChanged
方法,最后调用 mMethod.invoke(target)
方法实现回调
static class ObserverWithState { State mState; GenericLifecycleObserver mLifecycleObserver; ObserverWithState(LifecycleObserver observer, State initialState) { // 如果缓存里有 则直接从缓存中取,否则开始解析observer的@OnLifecycleEvent注解信息,并缓存起来 mLifecycleObserver = Lifecycling.getCallback(observer); mState = initialState; } void dispatchEvent(LifecycleOwner owner, Event event) { State newState = getStateAfter(event); mState = min(mState, newState); // 如果是反射的实现,则mLifecycleObserver 是ReflectiveGenericLifecycleObserver对象 mLifecycleObserver.onStateChanged(owner, event); mState = newState; } } class ReflectiveGenericLifecycleObserver implements GenericLifecycleObserver { private final Object mWrapped; private final CallbackInfo mInfo; ReflectiveGenericLifecycleObserver(Object wrapped) { mWrapped = wrapped; mInfo = ClassesInfoCache.sInstance.getInfo(mWrapped.getClass()); } @Override public void onStateChanged(LifecycleOwner source, Event event) { // 在invokeCallbacks方法中会调用 mMethod.invoke(target) 方法实现回调 mInfo.invokeCallbacks(source, event, mWrapped); } } 复制代码
可见实现一个运行时注解其实非常简单,直接使用反射获取注解信息,然后做相应的逻辑封装处理即可
运行时注解有一个很大的问题就是性能问题,因为使用了java的反射机制;所以这就需要你自己去做衡量了,去做各种优化处理了
下面我们来看看编译时注解
编译时注解是指注解处理器 (Annotation Processor)
在代码编译的过程中扫描和处理代码中的注解 (Annotation)
; 正所谓是在代码编译的过程中处理,所以一般对代码运行性能没什么影响;
由于编译时注解不能对已有的java类做任何修改,所以一般都是用来生成新的java文件来做相应的业务处理
创建自己的注解处理器
由于 Android
项目是没办法使用 javax
包下的 AbstractProcessor
类,所以必须要使用AndroidStudio新建一个 java library
( File -> new Module -> 选择Java Library
), 然后定义一个 AbstractProcessor
的子类, 并实现对应的方法
public class BindValueProcessor extends AbstractProcessor { /** * 初始化 * @param processingEnvironment */ @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); } /** * 支持的java版本号 * 推荐使用SourceVersion.latestSupported() */ @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } /** * 定义这个注解处理器处理哪些注解(必须重写,否则不会处理任何注解,即不会走process回调) */ @Override public Set<String> getSupportedAnnotationTypes() { Set<String> types = new LinkedHashSet<>(); types.add(BindValue.class.getCanonicalName()); return types; } /** * 基本上不需要重写这个方法 * 主要是定义在build.gradle文件下配置的arguments支持的参数 * 定义之后可以在process方法中使用processingEnv.getOptions().get(key)获取value; * javaCompileOptions { * annotationProcessorOptions { * arguments = [ key : 'value' ] * } * } */ @Override public Set<String> getSupportedOptions() { return super.getSupportedOptions(); } /** * 核心方法--在这个方法里解析注解信息,并生成新的java文件等 * @param annotations * @param roundEnvironment * @return */ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) { ... return true; } } 复制代码
对于 AbstractProcessor
的各个方法的解释请看对应方法上的注释,其中非常重要的方法是对于 process
方法的实现,下面会介绍 process
方法的实现
注册注解处理器
定义完你的注解处理器之后 (BindValueProcessor)
之后,然后就需要注册你的注解处理器
在 main
下新建 resources
文件夹,然后在 resources
下新建 META-INF
文件夹,然后在 META-INF
下新建 services
文件夹,最后在 services
下新建 javax.annotation.processing.Processor
文件;即文件结构为 main/resources/META-INF/services/javax.annotation.processing.Processor
最后打开 javax.annotation.processing.Processor
文件将你的注解处理器添加到里面(包名+类名); 比如我demo中的 com.fc.annotation.annotation.BindValueProcessor
; 当编辑 javax.annotation.processing.Processor
文件的时候,其实 AndroidStudio
有提示, 会列出所有可配置的注解处理器,选择你自己的注解处理器即可
在app中使用你的注解处理器
直接在build.gradle添加依赖即可
dependencies { ... // 针对Java annotationProcessor project(":library") // 针对Kotlin kapt project(":library") } 复制代码
这里是直接依赖的你的上面新建的 Java Library
module, 你可以将这个 Library
发版到 maven
仓库,然后再去依赖 maven
仓库里的library, 这不是我要说的重点,所以对于怎么发版到 maven
仓库 请自己研究
到这里的话,整个流程都做的差不多了,现在就差 process
方法的具体实现了,这也是对于一个新手比较迷茫的地方,因为对于 javax.annotation.processing
包下的各种 Api
不熟,不知道怎么用;下面我们先来做一下 API 扫盲
工作
Messager对象(在控制台上打印日志)
对于日志打印,一般大家直接想到的是直接使用 Log
或 System.out.println()
, 当你用这些方法输出日志的时候,你会发现控制台没有任何你的日志;对于注解处理器,如果想要输出日志,就需要使用它提供的 Messager
对象
// 输出警告类型的日志 processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, message); // 输出错误类型的日志,会导致build失败 processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, message); // 输出记录类型的日志,大多数情况下不会控制台不会输出这种类型的日志 // 如果要输出这种类型的日志,则需要加上 --info 或 --debug(比如 ./gradlew assembleDebug --info) processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, message); 复制代码
常用的日志类型就这三种,大多数情况下控制台只会输出 WARNING
和 ERROR
类型的日志,如果要输出其它类型的日志,需要在gradlew命令上加上 --info 或 --debug
参数;
processingEnv
是 AbstractProcessor
的一个成员变量
知道输出日志后,我们就可以配合日志 来调试我们的代码了,下面介绍去怎么解析 Field
、 Method
、 Class
,分别对应注解 (Annotation)
在 Field
、 Method
、 Class
上的应用
解析 Field
- VariableElement
VariableElement
表示一个属性、enum 常量、方法或构造方法参数、局部变量或异常参数;
下面对于 Field
、 Method
、 Class
的解析都以下面的代码为例
@BindValue public class AnnotationActivity<T, K extends List> extends AppCompatActivity implements Runnable { @BindValue public String name; @BindValue public void test(View view, String a, List<String> list) { } ... } 复制代码
首先我们在process方法中可以获取被 @BindValue
注解的全部 Element
元素集合
@Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) { // 获取 被@BindValue注解的Element元素集合,下面主要介绍VariableElement、ExecutableElement、TypeElement Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindValue.class); ... } 复制代码
比如我们要解析被 @BindValue
注解的 name
属性
/** * 解析被注解的field * @param element */ private void resolveVariableElement(VariableElement element) { StringBuffer buffer = new StringBuffer(); // 获取修饰符 public、static、final等等 for (Modifier modifier : element.getModifiers()) { buffer.append(modifier.toString()).append(" "); } // 获取field 类型 buffer.append(element.asType().toString()).append(" "); // 获取field 名称 buffer.append(element.getSimpleName()); processingEnv.getMessager().printMessage("====VariableElement: "+ buffer.toString()); } 复制代码
输出的日志如下:
====VariableElement: public java.lang.String name 复制代码
由于 VariableElement
也可以表示方法的参数,所以 对于方法参数的解析可以使用上面的api
解析 Method
- ExecutableElement
ExecutableElement
表示某个类或接口的方法、构造方法或初始化程序(静态或实例),包括注释类型元素
比如我们要解析被 @BindValue
注解的 test
方法
/** * 解析被注解的method * @param element */ private void resolveExecutableElement(ExecutableElement element) { StringBuffer buffer = new StringBuffer(); // 获取修饰符 public、static、final等等 for (Modifier modifier : element.getModifiers()) { buffer.append(modifier.toString()).append(" "); } // 获取返回 类型 buffer.append(element.getReturnType().toString()).append(" "); // 获取方法 名称 buffer.append(element.getSimpleName()); // 获取方法 参数 buffer.append("("); List<? extends VariableElement> parameters = element.getParameters(); for (VariableElement parameterElement : parameters) { // 获取方法 参数类型和参数名 buffer.append(parameterElement.asType().toString() + " " + parameterElement.getSimpleName() + ", "); } buffer.append(")"); println("====ExecutableElement: "+ buffer.toString()); } 复制代码
输出的日志如下:
====ExecutableElement: public void test(android.view.View view, java.lang.String a, java.util.List<java.lang.String> list) 复制代码
解析 Class
- TypeElement
TypeElement
表示一个类或接口程序元素
比如我们要解析被 @BindValue
注解的 AnnotationActivity
类的信息
/** * 解析被注解的class、interface、enum * @param element */ private void resolveTypeElement(TypeElement element) { StringBuffer buffer = new StringBuffer(); // 获取修饰符 public、static、final等等 for (Modifier modifier : element.getModifiers()) { buffer.append(modifier.toString()).append(" "); } // 获取被注解的class 是类、接口、还是枚举 if (element.getKind() == ElementKind.CLASS) { // class buffer.append(element.getKind().name().toLowerCase()).append(" "); } else if (element.getKind() == ElementKind.INTERFACE) { // interface buffer.append(element.getKind().name().toLowerCase()).append(" "); } else if (element.getKind() == ElementKind.ENUM) { // enum buffer.append(element.getKind().name().toLowerCase()).append(" "); } // 获取class 名称 buffer.append(element.getSimpleName()); // 获取定义的class范型信息(即"<>"指定的信息) List<? extends TypeParameterElement> patterns = element.getTypeParameters(); if (patterns != null && !patterns.isEmpty()) { buffer.append("<"); for (TypeParameterElement typeParameterElement : patterns) { DeclaredType typeMirror = (DeclaredType) typeParameterElement.getBounds().get(0); if (typeMirror.toString().equals("java.lang.Object")) { // 如果没有指定范型的类型,则是Object buffer.append(typeParameterElement.getSimpleName()); } else { buffer.append(typeParameterElement.getSimpleName() + " extends " + typeMirror.asElement().getSimpleName()); } buffer.append(", "); } buffer.delete(buffer.length() - 2, buffer.length()); buffer.append(">"); } buffer.append(" "); // 获取继承的父类信息 TypeMirror superMirror = element.getSuperclass(); if (superMirror instanceof DeclaredType) { // 父类也是TypeElement类型,所以直接强转 TypeElement superElement = (TypeElement) processingEnv.getTypeUtils().asElement(superMirror); // 又可以 使用resolveTypeElementSet解析父类 // resolveTypeElementSet(superElement); buffer.append("extends " + superElement.getSimpleName()).append(" "); // 解析父类的范型信息 如上操作即可 } // 获取实现的接口 List<? extends TypeMirror> interfaceTypeMirror = element.getInterfaces(); if (interfaceTypeMirror != null && !interfaceTypeMirror.isEmpty()) { buffer.append("implements").append(" "); for (TypeMirror typeMirror : interfaceTypeMirror) { // 接口也是TypeElement类型,所以直接强转 TypeElement interfaceElement = (TypeElement) processingEnv.getTypeUtils().asElement(typeMirror); // 又可以 使用resolveTypeElementSet解析interface // resolveTypeElementSet(interfaceElement); buffer.append(interfaceElement.getSimpleName()).append(", "); } // 删除最后的", " buffer.delete(buffer.length() - 2, buffer.length()); } println("====TypeElement: "+ buffer.toString()); } 复制代码
输出日志如下:
====TypeElement: public class AnnotationActivity<T, K extends List> extends AppCompatActivity implements Runnable 复制代码
上面的代码对于class的解析比较长,因为写的比较全,考虑到了父类、接口、范型等等,对于 Feild
和 Method
的解析 如果需要考虑范型,也可以使用上面的 getTypeParameters()
去处理
最后再看看 process
现在的实现
@Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) { for (TypeElement annotation : annotations) { Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(annotation); for (Element element : elements) { if (element instanceof ExecutableElement) { resolveExecutableElement((ExecutableElement) element); } else if (element instanceof VariableElement) { resolveVariableElement((VariableElement) element); } else if (element instanceof TypeElement) { resolveTypeElement((TypeElement) element); } else { } } } return true; } 复制代码
Filer对象(生成java文件)
Filer
对象的 API
比较少,用起来也比较简单,常用来生成文件, 比如
try { Filer filer = processingEnv.getFiler(); JavaFileObject sourceFile = filer.createSourceFile("Test"); BufferedWriter writer = new BufferedWriter(sourceFile.openWriter()); writer.write("public class Test {/n"); writer.write(" public static void main(String[] args) {/n"); writer.write(" System.out.println(/"test/");/n"); writer.write(" }/n"); writer.write("}/n"); writer.flush(); } catch (IOException e) { e.printStackTrace(); } 复制代码
在实际的应用,一般结合第三方的 javapoet 工具生成java文件
上面的代码只是介绍了怎么去解析 Field
、 Method
、 Class
上的信息,下面我们就要根据这些信息去生成 Java
文件了
Java
文件 下面就以一个简单的Demo为例;比如 对于 下面的java文件
public class AnnotationActivity extends AppCompatActivity { @BindValue("张三") public String name; @BindValue("李四") public String name2; ... } 复制代码
需要生成如下java文件:
public final class AnnotationActivity_Binding { public final void bind(AnnotationActivity target) { target.name = "张三"; target.name2 = "李四"; } } 复制代码
这里是使用 javapoet
工具生成java文件,对于 javapoet
的api文档(文档写的比较全),请自行去github查看,
先定义生成一个类的实现
// 缓存 TypeSpec.Builder private Map<Name, TypeSpec.Builder> typeSpecCache = new HashMap<>(); // 定义一个生成class的 TypeSpec.Builder private TypeSpec.Builder createOrGetTypeSpecBuilder(TypeElement typeElement) { Name name = typeElement.getQualifiedName(); TypeSpec.Builder typeSpecBuilder = typeSpecCache.get(name); if (typeSpecBuilder == null) { // 定义新的class的类名(原类名+"_Binding") // (即: public final class AnnotationActivity_Binding) typeSpecBuilder = TypeSpec.classBuilder(typeElement.getSimpleName() + "_Binding") .addModifiers(Modifier.PUBLIC, Modifier.FINAL); typeSpecCache.put(name, typeSpecBuilder); } return typeSpecBuilder; } 复制代码
再定义生成一个方法的实现
// 缓存 MethodSpec.Builder private Map<Name, MethodSpec.Builder> bindMethodCache = new HashMap<>(); // 定义一个生成bind方法的 MethodSpec.Builder private MethodSpec.Builder createOrGetBindMethodSpecBuilder(VariableElement element) { Name name = ((TypeElement) element.getEnclosingElement()).getQualifiedName(); MethodSpec.Builder builder = bindMethodCache.get(name); if (builder == null) { // 通过 element.getEnclosingElement() 可以获取到 name 属性所在的 class信息(即AnnotationActivity) TypeName typeName = TypeName.get(element.getEnclosingElement().asType()); if (typeName instanceof ParameterizedTypeName) { // 处理范型(如果AnnotationActivity支持范型,则一定要处理) typeName = ((ParameterizedTypeName) typeName).rawType; } // 定义bind方法(即: public final void bind(AnnotationActivity target)) builder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addParameter(typeName, "target"); bindMethodCache.put(name, builder); } return builder; } 复制代码
然后再给bind方法添加参数和方法体的具体实现
private void createAndImplBindMethod(VariableElement element) { // 检查是不是private属性 boolean privateFlag = false; for (Modifier modifier : element.getModifiers()) { if (modifier == Modifier.PRIVATE) { privateFlag = true; break; } } BindValue bindValue = element.getAnnotation(BindValue.class); MethodSpec.Builder methodSpecBuilder = createOrGetBindMethodSpecBuilder(element); if (privateFlag) { // 如果是private,就使用set方法赋值 String name = element.getSimpleName().toString(); String fieldSetterName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1); methodSpecBuilder.addStatement("target.$N($S)", fieldSetterName, bindValue.value()); } else { // 否则直接使用属性赋值 methodSpecBuilder.addStatement("target.$N = $S", element.getSimpleName(), bindValue.value()); } } 复制代码
生成java文件
private void createJavaFile(Set<? extends Element> elements) { for (Element element : elements) { if (element instanceof VariableElement) { createOrGetTypeSpecBuilder((TypeElement) element.getEnclosingElement()); createAndImplBindMethod((VariableElement) element); } } createFile(); } private void createFile() { for (Name name : typeSpecCache.keySet()) { TypeSpec.Builder typeSpecBuilder = typeSpecCache.get(name); MethodSpec.Builder methodBuilder = bindMethodCache.get(name); if (methodBuilder != null) { typeSpecBuilder.addMethod(methodBuilder.build()); } // 生成的java文件,与被注解的java文件在同一个package下 String packageName = name.toString().substring(0, name.toString().lastIndexOf(".")); JavaFile javaFile = JavaFile.builder(packageName, typeSpecBuilder.build()).build(); try { // 使用javapoet工具的api生成java文件 javaFile.writeTo(processingEnv.getFiler()); } catch (IOException e) { e.printStackTrace(); } } } 复制代码
所以最后的process的实现如下:
@Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) { for (TypeElement annotation : annotations) { Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(annotation); // 之前写测试代码已没用,直接注释掉 // for (Element element : elements) { // if (element instanceof ExecutableElement) { // resolveExecutableElement((ExecutableElement) element); // } else if (element instanceof VariableElement) { // resolveVariableElement((VariableElement) element); // } else if (element instanceof TypeElement) { // resolveTypeElement((TypeElement) element); // } else { // println("====BindValueProcessor other===="); // } // } createJavaFile(elements); } return true; } 复制代码
使用新生成的Java文件
public class BindValueManager { public static void bind(Object target) { String className = target.getClass().getName() + "_Binding"; try { Class clazz = Class.forName(className); Constructor constructor = clazz.getConstructor(); // 创建AnnotationActivity_Binding对象 Object object = constructor.newInstance(); Method method = clazz.getDeclaredMethod("bind", target.getClass()); // 调用AnnotationActivity_Binding对象的bind方法 method.invoke(object, target); } catch (Exception e) { e.printStackTrace(); } } } @BindValue public class AnnotationActivity extends AppCompatActivity { @BindValue("张三") public String name; @BindValue("李四") public String name2; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_annotation); // 重要 BindValueManager.bind(this); } ... } 复制代码
到这里,编译时注解运行的基本原理和使用方法介绍的差不多了, 至于编译时注解的android案例有很多,比如 Butterknife
、 EventBus
(也支持编译生成索引文件)、 dagger
等等,这里我就不一一介绍了,请自行阅读源码
由于 Javax
的 API
对于 Android
开发,基本上很少用,所以对其可能会不熟悉,而我上面只是简单的介绍了一下常用的api,所以其它的api请自行查看文档java API 中文文档