采用APT&RxJava造一辆RxBus

来源:互联网 发布:sql数据库编程 编辑:程序博客网 时间:2024/06/05 04:28

首先感谢以下文章:
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 insteadprivate 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简单记录,后期补充完整,先回家过年。

体验地址:
https://github.com/Neacy/RxBus_

1 0