对于注解( 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 中文文档