首先感谢以下文章:
APT:https://github.com/lizhaoxuan/Android-APT-Framework
反射:http://www.cnblogs.com/lzq198754/p/5780331.html
注解:http://www.cnblogs.com/linjiqin/archive/2011/02/16/1956426.html
还记当时在看RxJava的时候,有一个RxBus项目,而且没几行代码就能实现跟EventBus一样的功能,大概的代码如下:
public class RxBus {
//private final PublishSubject<Object> _bus = PublishSubject.create();
// If multiple threads are going to emit events to this
// then it must be made thread-safe like this instead
private final Subject<Object, Object> _bus = new SerializedSubject<>(PublishSubject.create());
public void send(Object o) {
_bus.onNext(o);
}
public Observable<Object> toObserverable() {
return _bus;
}
public boolean hasObservers() {
return _bus.hasObservers();
}
}
没错就是这么简单通过PublishSubject来发射事件,当然我们需要一个事件接收处理,通常如下:
_rxBus.toObserverable()
.subscribe(new Action1<Object>() {
@Override
public void call(Object event) {
if (event instanceof RxBusDemoFragment.TapEvent) {
_showTapText();
}
}
})
是的event就是我们收到的事件,这样虽然简单但是有个诟病,那就是一旦PublishSubject发射事件任何一个subscribe方法都会被调用,这也是为什么event instanceof RxBusDemoFragment.TapEvent需要这么一句来判断事件的类型。所以我们需要改良一下,将收到的Event事件只传递给具体注册了的界面去,类似于EventBus中的:
public void onEvent(Event event) {
// code..
}
所以在看了EventBus的源码和Butternife源码后想按他们的思路在编译时加入代码,主要用到的知识点就是开头涉及的那些连接知识点。
整个项目的大概结构是:
注册-注销-接受事件
@Subscribe
@BindRxBus
public void onEvent(Event event) {
Toast.makeText(this, "on Event", Toast.LENGTH_LONG).show();
}
@Override
protected void onCreate() {
super.onStart();
RxBus.getInstance().register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
RxBus.getInstance().unregister(this);
}
发送消息
RxBusDao.getInstance().post(new Event());
运行时注解
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface BindRxBus {
// int value() default -1;
}
当Retention = RetentionPolicy.CLASS的时候,编译时注解,在编译时被识别并处理的注解。
APT代码生成
通过注解,获取必要信息,在项目中生成代码,运行时调用,和直接运行手写代码没有任何区别。
@AutoService(Processor.class)
public class RxBusProcesser extends AbstractProcessor {
private static final String TAG = "RxBusProcesser";
private Filer mFiler;// 文件相关辅导类
private Elements mElements;// 元素相关
private Messager mMessager;// 日志
private Map<String, GenerateClass> mAnnotatedClassMap = new HashMap<>();
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
System.out.println("==== RxBusProcesser -> init ====");
mFiler = processingEnvironment.getFiler();
mElements = processingEnvironment.getElementUtils();
mMessager = processingEnvironment.getMessager();
}
/**
* 声明支持的注释
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
System.out.println("==== RxBusProcesser -> getSupportedAnnotationTypes ====");
Set<String> types = new LinkedHashSet<>();
types.add(BindRxBus.class.getCanonicalName());
return types;
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
System.out.println("==== RxBusProcesser -> process ====");
processBindRxBus(roundEnvironment);
for (GenerateClass annotatedClass : mAnnotatedClassMap.values()) {
try {
info("Generating file for %s", annotatedClass.getFullClassName());
annotatedClass.generateJavaFile().writeTo(mFiler);
} catch (IOException e) {
System.out.println("Generate file failed, reason: %s" + e.getMessage());
return true;
}
}
return true;
}
private void processBindRxBus(RoundEnvironment roundEnvironment) {
for (Element element : roundEnvironment.getElementsAnnotatedWith(BindRxBus.class)) {
if (element.getKind() == ElementKind.METHOD) {
GenerateClass generateClass = getAnnotatedClass(element);
}
}
}
private GenerateClass getAnnotatedClass(Element element) {
TypeElement classElement = (TypeElement) element.getEnclosingElement();
String fullClassName = classElement.getQualifiedName().toString();
GenerateClass annotatedClass = mAnnotatedClassMap.get(fullClassName);
if (annotatedClass == null) {
annotatedClass = new GenerateClass(classElement, mElements);
mAnnotatedClassMap.put(fullClassName, annotatedClass);
}
return annotatedClass;
}
private void info(String msg, Object... args) {
mMessager.printMessage(Diagnostic.Kind.NOTE, String.format(msg, args));
}
}
要看懂代码需要了解一下xxxElement是什么意思。
package com.example;
public class Foo { // TypeElement
private int a; // VariableElement
private Foo other; // VariableElement
public Foo() {} // ExecuteableElement
public void setA( // ExecuteableElement
int newA // TypeElement
) {
}
}
感谢http://qiushao.net/2015/07/07/Annotation-Processing-Tool详解/
然后通过GenerateClass对象来生成代码:
public JavaFile generateJavaFile() {
// 构建inject函数 public inject(final )
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("inject")// 函数名称
.addModifiers(Modifier.PUBLIC)// 函数声明类型
.addAnnotation(Override.class)// 添加注释
.addParameter(TypeName.get(typeElement.asType()), "host", Modifier.FINAL);// 函数参数名
// 在inject函数中插入RxBus.register函数
methodBuilder.addStatement("$T.getInstance().register(host)", TypeUtil.RXBUS);
// 生成一个xxx$$RxBus类
TypeSpec finderClass = TypeSpec.classBuilder(typeElement.getSimpleName() + "$$RxBus")
.addModifiers(Modifier.PUBLIC)
.addSuperinterface(ParameterizedTypeName.get(TypeUtil.IRXBUS, TypeName.get(typeElement.asType())))
.addMethod(methodBuilder.build())
.build();
String packageName = elements.getPackageOf(typeElement).getQualifiedName().toString();
return JavaFile.builder(packageName, finderClass).build();
}
封装使用
IRxBus iRxBus = mRxBuses.get(className);
if (iRxBus == null) {
Class<?> _class = Class.forName(className + "$$RxBus");
iRxBus = (IRxBus) _class.newInstance();
mRxBuses.put(className, iRxBus);
}
iRxBus.inject(o);
通过少量的反射,把一些复杂的反射以及代码在编译时就给生成出来,大大的增加了速度。
最后:学习APT简单记录,后期补充完整,先回家过年。