EventBus简化

本文介绍了一款仿EventBus的事件发布/订阅框架MyEventBus。该框架简化了事件订阅流程,支持直接指定订阅者方法,提高了执行效率。文章详细展示了MyEventBus的核心代码实现,包括事件注册、取消注册及事件发布的逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

仿EventBus写的事件总线工具,主要由MyEventBus实现事件订阅post等逻辑,其他EventBusException、Subscribe、SubscriberMethod、ThreadMode直接从EventBus拿来用的。



实现:
在这里插入图片描述







MyEventBus代码

package com.david.core.event;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 仿eventbus的事件发布/订阅框架,对比eventBus添加了直接对订阅者方法订阅的功能(执行时间比遍历方法更快些)。
 *
 * example:
 * 参考:
 * {@link com.david.core.event.TestEvent}
 * {@link com.david.basemodule.MainActivity}
 *
 *
 * 注册:
 *
 * MyEventBus.getDefault().register(this, "testEvent", TestEvent::class.java)
 * {@link com.david.basemodule.MainActivity#onCreate(Bundle)}
 *
 * 解注册:
 * MyEventBus.getDefault().unregister(this, "testEvent", TestEvent::class.java)
 * {@link com.david.basemodule.MainActivity#onDestroy()}
 *
 * 发送:
 * MyEventBus.getDefault().post(new TestEvent());
 * {@link #post(Object)}
 *
 * 接收方法:
 * {@link com.david.basemodule.MainActivity#testEvent(TestEvent)}
 *
 *
 * 注:相比于EventBus没有粘性event及自动搜索父类订阅方法等一些功能,但MyEventBus理论上应该能满足平时80-90%的需求
 * 
 * 由于使用了方法名,因此在混淆时订阅方法不要混淆,如在混淆规则中添加:
 * -keep public class com.david.andserver.MainActivity {
 *     public void onServerStatus(...);
 *     //或public void on**(...);
 * }
 * */
public class MyEventBus {
    private volatile static MyEventBus defaultInstance;

    /*
     * In newer class files, compilers may add methods. Those are called bridge or synthetic methods.
     * EventBus must ignore both. There modifiers are not public but defined in the Java class file format:
     * http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.6-200-A.1
     */
    private static final int BRIDGE = 0x40;
    private static final int SYNTHETIC = 0x1000;

    private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;

    private ExecutorService fixedExecutorService;


    /**
     * loop()运行在主线程的handler,用于切换到主线程
     * */
    private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());

    /**
     * event与method订阅方法对应的map,post过来event之后遍历执行对应列表里的方法
     * */
    private volatile Map<Class, ArrayList<SubscriberMethod>> eventMethodMap = new HashMap<>();

    public static MyEventBus getDefault() {
        MyEventBus instance = defaultInstance;
        if (instance == null) {
            synchronized (MyEventBus.class) {
                instance = MyEventBus.defaultInstance;
                if (instance == null) {
                    instance = MyEventBus.defaultInstance = new MyEventBus();
                }
            }
        }
        return instance;
    }

    /**
     * 根据方法名注册,相对于遍历方法节省时间(对于类中订阅方法少的类较适用)
     * */
    public void register(Object subscriber, String methodName, Class eventclass) {
        try {
            Method method = subscriber.getClass().getDeclaredMethod(methodName, eventclass);

            int modifiers = method.getModifiers();
            if ((modifiers & Modifier.PUBLIC) == 0 || (modifiers & MODIFIERS_IGNORE) != 0) {
                throw new EventBusException(subscriber.getClass().getName() +"."+methodName +
                        " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
            }

            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length != 1) {
                throw new EventBusException("@Subscribe method " + methodName +
                        "must have exactly 1 parameter but has " + parameterTypes.length);
            } else if (!parameterTypes[0].getName().equals(eventclass.getName())) {
                /*订阅方法中的event参数与注册方法中传入的event参数不对应*/
                throw new EventBusException("method parameter " + parameterTypes[0].getName() +
                        " is not equals the input eventType:" + eventclass.getName());
            }

            addMethodToMap(subscriber, eventclass, method);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    private void addMethodToMap(Object subscriber, Class eventclass, Method method) {
        Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
        if (subscribeAnnotation == null) {
            Log.w("eventbus", subscriber.getClass().getName() + "." +method.getName() + "'s didn't has annotation of Subscribe.");
            return;
        }
        if (eventMethodMap.get(eventclass) == null) {
            ArrayList<SubscriberMethod> methods = new ArrayList<>();
            methods.add(new SubscriberMethod(method, subscribeAnnotation.threadMode(), subscriber));
            eventMethodMap.put(eventclass, methods);
        } else {
            SubscriberMethod subscriberMethod = new SubscriberMethod(method, subscribeAnnotation.threadMode(), subscriber);
            if (!eventMethodMap.get(eventclass).contains(subscriberMethod)) {
                eventMethodMap.get(eventclass).add(subscriberMethod);
            }
        }
    }

    /***
     * @param subscriber 订阅者
     */
    public void register(Object subscriber){
        findMethodAndRegister(subscriber);
    }

    /**
     * 查找类中订阅方法并添加到map中,TODO 查找并添加父类中的订阅方法
     * */
    private void findMethodAndRegister(Object subscriber) {
        Method[] methods;
        try {
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
            methods = subscriber.getClass().getDeclaredMethods();
        } catch (Throwable th) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            th.printStackTrace();
            return;
        }
        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) {
                        addMethodToMap(subscriber, parameterTypes[0], method);
                    }
                } else if (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 (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");
            }
        }
    }

    /**
     * post方法:获取对应的订阅方法列表并遍历在相应线程执行
     * */
    public void post(Object eventObject) {
        ArrayList<SubscriberMethod> subscriberMethods = eventMethodMap.get(eventObject.getClass());
        if (subscriberMethods == null) {
            Log.w("eventbus", "the map of eventMethodMap is empty.");
            return;
        }
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            try {
                if (subscriberMethod.threadMode == ThreadMode.ASYNC) {
                    invokeInSubThread(eventObject, subscriberMethod);
                } else if (subscriberMethod.threadMode == ThreadMode.BACKGROUND) {
                    if (Looper.getMainLooper() != Looper.myLooper()) {
                        subscriberMethod.method.invoke(subscriberMethod.subscriber, eventObject);
                    } else {
                        invokeInSubThread(eventObject, subscriberMethod);
                    }
                } else if ((subscriberMethod.threadMode == ThreadMode.MAIN || subscriberMethod.threadMode == ThreadMode.MAIN_ORDERED)) {
                    if (Looper.getMainLooper() != Looper.myLooper()) {
                        //mode为MAIN则post到主线程执行
                        invokeInMainThread(eventObject, subscriberMethod);
                    } else {
                        subscriberMethod.method.invoke(subscriberMethod.subscriber, eventObject);
                    }
                } else {//POSTING
                    //mode为其他则直接在当前线程执行
                    subscriberMethod.method.invoke(subscriberMethod.subscriber, eventObject);
                }
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    private void invokeInSubThread(Object eventObject, SubscriberMethod subscriberMethod) {
        if (fixedExecutorService == null) {
            fixedExecutorService = Executors.newFixedThreadPool(3);
        }
        fixedExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    subscriberMethod.method.invoke(subscriberMethod.subscriber, eventObject);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private void invokeInMainThread(Object eventObject, SubscriberMethod subscriberMethod) {
        mainThreadHandler.post(new Runnable() {
            @Override
            public void run() {
                try {
                    subscriberMethod.method.invoke(subscriberMethod.subscriber, eventObject);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /***
     * @param subscriber 订阅者
     */
    public void unregister(Object subscriber){
        Set<Class> keys = eventMethodMap.keySet();
        HashMap<Class, ArrayList<SubscriberMethod>> tempMap = new HashMap<>();
        for (Class key:keys) {
            ArrayList<SubscriberMethod> subscriberMethods = eventMethodMap.get(key);
            ArrayList<SubscriberMethod> tempMethods = new ArrayList<>();

            for (SubscriberMethod item:subscriberMethods) {
                if (item.subscriber == subscriber) {
                    tempMethods.add(item);
                }
            }

            subscriberMethods.removeAll(tempMethods);
            if (subscriberMethods.size() > 0) {
                tempMap.put(key, subscriberMethods);
            }
        }
        eventMethodMap = tempMap;
    }

    public void unregister(Object subscriber, String methodName, Class eventClass) {
        ArrayList<SubscriberMethod> subscriberMethods = eventMethodMap.get(eventClass);
        if (subscriberMethods != null) {

            ArrayList<SubscriberMethod> removeList = new ArrayList<>();
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                if (methodName.equals(subscriberMethod.getMethod().getName()) && subscriberMethod.subscriber == subscriber) {
                    removeList.add(subscriberMethod);
                }
            }
            subscriberMethods.removeAll(removeList);
            if (subscriberMethods.size() == 0) {
                eventMethodMap.remove(eventClass);
            }
        }
    }
}

EventBusException代码

package com.david.core.event;
/**
 * An {@link RuntimeException} thrown in cases something went wrong inside EventBus.
 *
 * @author Markus
 *
 */
public class EventBusException extends RuntimeException {

    private static final long serialVersionUID = -2912559384646531479L;

    public EventBusException(String detailMessage) {
        super(detailMessage);
    }

    public EventBusException(Throwable throwable) {
        super(throwable);
    }

    public EventBusException(String detailMessage, Throwable throwable) {
        super(detailMessage, throwable);
    }

}
package com.david.core.event;


import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;


    /** Subscriber priority to influence the order of event delivery.
     * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
     * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
     * delivery among subscribers with different {@link ThreadMode}s! */
    int priority() default 0;
}

SubscriberMethod代码

package com.david.core.event;

import java.lang.reflect.Method;
import java.util.Objects;

/**
 * 每个订阅事件信息类
 * */
class SubscriberMethod {
    /**订阅回调的方法*/
     Method method;
    /**回调执行线程信息*/
    ThreadMode threadMode;
    /**订阅者,调用{@link com.david.core.event.MyEventBus#register(Object)}的方法的类*/
    Object subscriber;

    public SubscriberMethod() {
    }

    public SubscriberMethod(Method method, ThreadMode threadMode, Object subscriber) {
        this.method = method;
        this.threadMode = threadMode;
        this.subscriber = subscriber;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    public ThreadMode getThreadMode() {
        return threadMode;
    }

    public void setThreadMode(ThreadMode threadMode) {
        this.threadMode = threadMode;
    }

    public Object getSubscribeObject() {
        return subscriber;
    }

    public void setSubscribeObject(Object subscribeObject) {
        this.subscriber = subscribeObject;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof SubscriberMethod)) return false;
        SubscriberMethod that = (SubscriberMethod) o;
        return Objects.equals(getMethod(), that.getMethod()) &&
                getThreadMode() == that.getThreadMode() &&
                Objects.equals(subscriber, that.subscriber);
    }

    @Override
    public int hashCode() {
        return Objects.hash(getMethod(), getThreadMode(), subscriber);
    }
}

ThreadMode代码

package com.david.core.event;

/**
 * Each subscriber method has a thread mode, which determines in which thread the method is to be called by EventBus.
 * EventBus takes care of threading independently from the posting thread.
 *
 * @see MyEventBus#register(Object, String, Class)
 * @author Markus
 */
public enum ThreadMode {
    /**
     * Subscriber will be called directly in the same thread, which is posting the event. This is the default. Event delivery
     * implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for
     * simple tasks that are known to complete in a very short time without requiring the main thread. Event handlers
     * using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.
     */
    POSTING,

    /**
     * On Android, subscriber will be called in Android's main thread (UI thread). If the posting thread is
     * the main thread, subscriber methods will be called directly, blocking the posting thread. Otherwise the event
     * is queued for delivery (non-blocking). Subscribers using this mode must return quickly to avoid blocking the main thread.
     * If not on Android, behaves the same as {@link #POSTING}.
     */
    MAIN,

    /**
     * On Android, subscriber will be called in Android's main thread (UI thread). Different from {@link #MAIN},
     * the event will always be queued for delivery. This ensures that the post call is non-blocking.
     */
    MAIN_ORDERED,

    /**
     * On Android, subscriber will be called in a background thread. If posting thread is not the main thread, subscriber methods
     * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single
     * background thread, that will deliver all its events sequentially. Subscribers using this mode should try to
     * return quickly to avoid blocking the background thread. If not on Android, always uses a background thread.
     */
    BACKGROUND,

    /**
     * Subscriber will be called in a separate thread. This is always independent from the posting thread and the
     * main thread. Posting events never wait for subscriber methods using this mode. Subscriber methods should
     * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number
     * of long running asynchronous subscriber methods at the same time to limit the number of concurrent threads. EventBus
     * uses a thread pool to efficiently reuse threads from completed asynchronous subscriber notifications.
     */
    ASYNC
}







不足之处敬请指出,万分感谢🙏。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值