Android进阶之路Android开发Android技术知识

EventBus原理解析,纯手工带你撸代码实现EventBus框

2019-03-29  本文已影响8人  MR_特殊人士

github源码分享:https://github.com/greenrobot/EventBus

官方图解:

EventBus是Android和Java的发布/订阅事件总线。

EventBus优势:

1、简化了组件之间的通信:

将事件发送者和接收者分离

在活动,片段和后台线程中表现良好

避免复杂且容易出错的依赖关系和生命周期问题

2、使您的代码更简洁

3、很快

4、很小(约50kb)

5、已经通过100,000,000+安装的应用程序在实践中得到证实

6、具有交付线程,用户优先级等高级功能;

总结:

EventBus的使用比较简单,可以按照官网图的方式来进行理解。EventBus相当于邮箱,发布者直接把信息放到信箱,信箱会自动把信息发给订阅了的人。

这里简单梳理一下EventBus的主要逻辑:

1、EventBus对象的构建,使用单列模式创建

2、注册事件,包含粘性事件

3、发布消息

4、接受消息

5、反注册事件,避免内存泄漏

(一)、EventBus构造方法原理

这里就简单的使用单例模式实现

EventBus.java

private static EventBus instance;

public static EventBus getDefault() {

    if (instance == null) {

        synchronized (EventBus.class) {

            if (instance == null) {

                instance = new EventBus();

            }

        }

    }

    return instance;

}

(二)、注册事件

注册就是将此类中的所有的带Subscriber注解的方法缓存起来,以便发布消息时调用;

我们也是简单的模拟实现注册,不包含粘性事件的注册

需求:从AActivity跳转到BActivity中,在BActivity中发布消息给AActivity;

//比如在AActivity接受事件,我们就在AActivity中注册一个接收消息的方法

如:

MainActivity.java

@Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

  //注册

    EventBus.getDefault().register(this);

}

@Subscribe(threadMode = ThreadMode.BACKGROUND)

public void handleMessages(EventBean eventBean) {

    Log.d("======>", eventBean.toString());

    Log.d("======", "send: MainActivity " + Thread.currentThread().getName());

}

Subscribe是一个注解类,用来标记一个方法

Subscribe.java

@Documented

@Retention(RetentionPolicy.RUNTIME)// 表示为运行时注解

@Target({ElementType.METHOD})

public @interface Subscribe {

    // 指定事件订阅方法的线程模式,即在那个线程执行事件订阅方法处理事件,默认为POSTING

    ThreadMode threadMode() default ThreadMode.POSTING;

    // 是否支持粘性事件,默认为false

    boolean sticky() default false;

    // 指定事件订阅方法的优先级,默认为0,如果多个事件订阅方法可以接收相同事件的,则优先级高的先接收到事件

    int priority() default 0;

}

ThreadMode是一个枚举类,用来标记此方法在那个线程执行

ThreadMode.java

public enum ThreadMode {

    POSTING,//默认的线程模式,在那个线程发送事件就在对应线程处理事件,避免了线程切换,效率高。

    MAIN,//如在主线程(UI线程)发送事件,则直接在主线程处理事件;如果在子线程发送事件,则先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件。

    MAIN_ORDERED,//无论在那个线程发送事件,都先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件。

    BACKGROUND,//如果在主线程发送事件,则先将事件入队列,然后通过线程池依次处理事件;如果在子线程发送事件,则直接在发送事件的线程处理事件。

    ASYNC//无论在那个线程发送事件,都将事件入队列,然后通过线程池处理。

}

private Map<Object, List<SubscriberMethod>> cacheMap;// 用来缓存注册\订阅的事件

public void register(Object subscriber) {

    // 检查此类是否已经注册

    List<SubscriberMethod> subscriberMethods = cacheMap.get(subscriber);

    if (subscriberMethods == null) {

        subscriberMethods = findSubscriberMethods(subscriber);

        cacheMap.put(subscriber, subscriberMethods);

    }

}

/**

* 查找此类以及父类中所有带Subscriber注释的方法

* @param subscriber

* @return

*/

private List<SubscriberMethod> findSubscriberMethods(Object subscriber) {

    List<SubscriberMethod> list = new ArrayList<>();

    Class<?> clazz = subscriber.getClass();

    while (clazz != null) {

        String name = clazz.getName();

        // 过滤系统类

        if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {

            break;

        }

        Method[] methods = clazz.getDeclaredMethods();// 获取此类中所有的的方法(包含继承父类中的)

        for (Method method : methods) {

            Subscribe annotation = method.getAnnotation(Subscribe.class);// 找到方法上的注解参数

            if (annotation == null) {

                continue;

            }

            //获取所有带有Subscribe注解的方法中的参数类型

            Class<?>[] parameterTypes = method.getParameterTypes();

            if (parameterTypes.length != 1) {// 只记录带有一个参数的方法

                Log.e("error", "EventBus only support one para");

                continue;

            }

            ThreadMode threadMode = annotation.threadMode();

            SubscriberMethod subscriberMethod = new SubscriberMethod(method, parameterTypes[0], threadMode, 0, false);

            list.add(subscriberMethod);

        }

        clazz = clazz.getSuperclass();

    }

    return list;

}

SubscriberMethod.java 带有注解的方法对象

public class SubscriberMethod {

    private Method method;

    private ThreadMode threadMode;

    private Class<?> eventType;

    private int priority;

    private boolean sticky;

    /**

    * Used for efficient comparison

    */

    String methodString;

    public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {

        this.method = method;

        this.threadMode = threadMode;

        this.eventType = eventType;

        this.priority = priority;

        this.sticky = sticky;

    }

    @Override

    public boolean equals(Object other) {

        if (other == this) {

            return true;

        } else if (other instanceof SubscriberMethod) {

            checkMethodString();

            SubscriberMethod otherSubscriberMethod = (SubscriberMethod) other;

            otherSubscriberMethod.checkMethodString();

            // Don't use method.equals because of http://code.google.com/p/android/issues/detail?id=7811#c6

            return methodString.equals(otherSubscriberMethod.methodString);

        } else {

            return false;

        }

    }

    private synchronized void checkMethodString() {

        if (methodString == null) {

            // Method.toString has more overhead, just take relevant parts of the method

            StringBuilder builder = new StringBuilder(64);

            builder.append(method.getDeclaringClass().getName());

            builder.append('#').append(method.getName());

            builder.append('(').append(eventType.getName());

            methodString = builder.toString();

        }

    }

    @Override

    public int hashCode() {

        return method.hashCode();

    }

    public Method getMethod() {

        return method;

    }

    public ThreadMode getThreadMode() {

        return threadMode;

    }

    public Class<?> getEventType() {

        return eventType;

    }

    public int getPriority() {

        return priority;

    }

    public boolean isSticky() {

        return sticky;

    }

    public String getMethodString() {

        return methodString;

    }

}

以上就是事件注册\订阅的完整流程

(三)、发布消息

SecondActivity.java中任意位置

EventBus.getDefault().post(new EventBean("EventBusMassage", 111111111));

EventBus.java

public void post(final Object obj) {

    // 从注册事件中循环查询

    Set<Object> objects = cacheMap.keySet();

    Iterator<Object> iterator = objects.iterator();

    while (iterator.hasNext()) {

        final Object next = iterator.next();

        List<SubscriberMethod> list = cacheMap.get(next);

        for (final SubscriberMethod subsMethod : list) {

            // 获取到的类的类型是否和传入或者发送消息的类对象类型相同

            if (subsMethod.getEventType().isAssignableFrom(obj.getClass())) {

                switch (subsMethod.getThreadMode()) {

                    case MAIN:

                        // 主线程---》主线程

                        if (Looper.myLooper() == Looper.getMainLooper()) {

                            invoke(subsMethod, next, obj);

                        } else { // 子线程---》主线程

                            mHandler.post(new Runnable() {

                                @Override

                                public void run() {

                                    invoke(subsMethod, next, obj);

                                }

                            });

                        }

                        break;

                    case BACKGROUND:

                        // 主线程---》子线程

                        if (Looper.myLooper() == Looper.getMainLooper()) {

                            ExecutorService executorService = Executors.newFixedThreadPool(10);

                            executorService.execute(new Runnable() {

                                @Override

                                public void run() {

                                    invoke(subsMethod, next, obj);

                                }

                            });

                        } else { // 子线程---》子线程

                            invoke(subsMethod, next, obj);

                        }

                        break;

                }

            }

        }

    }

}

// 方法调用并执行

private void invoke(SubscriberMethod subsMethod, Object next, Object obj) {

    Method method = subsMethod.getMethod();

    try {

        method.invoke(next, obj);

    } catch (IllegalAccessException e) {

        e.printStackTrace();

    } catch (InvocationTargetException e) {

        e.printStackTrace();

    }

}

(四)、接受消息

MainActivity.java

@Subscribe(threadMode = ThreadMode.BACKGROUND)

public void handleMessages(EventBean eventBean) {

    Log.d("======>", eventBean.toString());

    Log.d("======", "handleMessages: MainActivity " + Thread.currentThread().getName());

}

(五)、注销事件

MainActivity.java

@Override

protected void onDestroy() {

    super.onDestroy();

    EventBus.getDefault().unregister(this);

}

EventBus.java

// 注销事件

public void unregister(Object subscriber) {

    List<SubscriberMethod> subscribedTypes = cacheMap.get(subscriber);

    if (subscribedTypes != null) {

        cacheMap.remove(subscriber);

    } else {

        Log.e("error", "Subscriber to unregister was not registered before: " + subscriber.getClass());

    }

}

参考文档:https://www.jianshu.com/p/d9516884dbd4

上一篇 下一篇

猜你喜欢

热点阅读