采用 APT&RxJava 造一辆 RxBus

907 阅读3分钟
原文链接: blog.csdn.net

首先感谢以下文章:
APT:github.com/lizhaoxuan/…
反射:www.cnblogs.com/lzq198754/p…
注解:www.cnblogs.com/linjiqin/ar…

还记当时在看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
    ) {
    }
}

感谢qiushao.net/2015/07/07/…
然后通过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简单记录,后期补充完整,先回家过年。

体验地址:
github.com/Neacy/RxBus…