【Android】EventBus详解

一,概述

EventBus即事件总线,在软件架构设计中,采用事件-发布-订阅类架构,通常具备高内聚低耦合特性,相对比通过Android#Broadcast方式,此框架由于不涉及跨进程方式,效率更加高效,并且提供了类似注解订阅方式,EventBus全局访问,不会被局限context组件。

笔者理解,这就是解耦框架,将不关联模块解耦,只通过总线方式通线,在企业级架构设计中,EventBus被经常使用。

二,实例

依赖

implementation "org.greenrobot:eventbus:3.3.1"

获取EventBus实例通常由默认方法getDefault,

或者通过build定制化参数获得,比如可以在build中添加SubscriberInfoIndex接口,自定义订阅方法,这就无需依赖Subscribe注解,

并且重写getSubscriberInfo方法即可

但笔者仍推荐Subscribe注解,但如果一个类中方法特别多,反射多方法存在性能开销,可以考虑index方式快速找到方法,

三,源码

1,register

1,通过subscriberFinder找到订阅方法,核心就在于怎么找,要么通过注解,要么通过index接口

2,对单个实例订阅该方法,订阅事件即方法参数

跟进findSubscribeMethods

1,从缓存查找,命中即返回

2,如果build参数ignoreGeneratedIndex为true,则直接通过反射查找,否则可以尝试index接口

3,index接口超找订阅方法

4,缓存

跟进findUsingInfo方法,

1,通过getSubscriberInfo找到SubscribeInfo,

2,如果没有index接口,则通过反射查找

跟进findUsingReflectionInSingleClass

1,反射所有声明方法

3,找到Subscribe注解方法

4,创建SubscribeMethod实体,保存此方法

最后,根据这个findState返回method

再将FindState放进对象池中,此处是享元模式设计,减少FindState创建数量

2,post

sticky粘性事件,sticky区分状态或事件,事件存在实时性,而状态存在唯一性,因此,状态一般是粘性,而事件则是非粘性,

在register中,通过订阅方法的sticky字段,决定是否将stickEvents中事件发布给订阅者,

stickyEvents需要显示添加,否则普通post仅是普通事件,无法加入stickyEvents Map

直接看post源码

1,从ThreadLocal中获得PostingThredState,其isPosing表示是否正在发送事件

当isPosing为false,则通过eventQueue出队消费事件,

跟进postSingleEvent

上述,存在可继承性事件判断,这在构造EventBus中设置,如下,

跟进postSingleEventForEventType

通过subscriptionEventType通过event快速获得订阅者,

这行map在register的subscribe中添加

跟进postToSubscription发布时间到subscription

根据线程模型,选择策略

POSTING:当前线程

MAIN:当前是主线程直接触发,或者加入主线程队列

MAIN_OROERED:加入主线程队列

BACKGROUND:子线程

ASYNC:反正异步,也是一个子线程

跟进invokeSubscribert

这就很简单了,method#invoke传入event即可,通知到订阅者

四,注意

避免内存泄漏,对于存在生命周期的对象,或需要销毁的对象,及时调用EventBus#unregister方法反注册,不然将作为Subscription#subscriber对象存在EventBus引用中。

五,非反射实现的EventBus

以下,笔者提供一份非反射实现的EventBus,仅供参考,

Event

package eventbus;

public final class Event {

    public Class<?> type;

    public Object payload;


    /**
     * @noinspection unchecked
     */
    public <T> T castPayload(Class<T> clazz) {
        return (T) payload;
    }

    public Object getPayload() {
        return payload;
    }
}

EventFilter

package eventbus;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public final class EventFilter {

    private final List<Class<?>> eventList = new ArrayList<>();
    private final Map<Class<?>, EventInfo> eventInfoMap = new HashMap<>();

    public void addEvent(Class<?> event) {
        addEvent(event, EventHandleThread.POST);
    }

    public void addEvent(Class<?> event, EventHandleThread eventHandleThread) {
        addEvent(event, eventHandleThread, false);
    }

    public void addEvent(Class<?> event, EventHandleThread eventHandleThread, boolean sticky) {
        addEvent(event, eventHandleThread, sticky, 0);
    }

    public void addEvent(Class<?> event, EventHandleThread eventHandleThread, boolean sticky, int priority) {
        if (!eventList.contains(event)) {
            eventList.add(event);
        }
        EventInfo eventInfo = new EventInfo();
        eventInfo.eventHandleThread = eventHandleThread;
        eventInfo.sticky = sticky;
        eventInfo.priority = priority;
        eventInfoMap.put(event, eventInfo);
    }

    public List<Class<?>> getEventList() {
        return eventList;
    }

    public EventHandleThread getEventHandleThread(Class<?> event) {
        EventInfo eventHandleThread = eventInfoMap.get(event);
        if (eventHandleThread == null) {
            return EventHandleThread.POST;
        }
        return eventHandleThread.eventHandleThread;
    }

    public int getEventPriority(Class<?> event) {
        EventInfo eventHandleThread = eventInfoMap.get(event);
        if (eventHandleThread == null) {
            return 0;
        }
        return eventHandleThread.priority;
    }

    public boolean isStickyEvent(Class<?> event) {
        EventInfo eventHandleThread = eventInfoMap.get(event);
        if (eventHandleThread == null) {
            return false;
        }
        return eventHandleThread.sticky;
    }

    private static class EventInfo {
        public int priority;
        EventHandleThread eventHandleThread;
        boolean sticky;
    }
}

EventSubscriber

package eventbus;

@FunctionalInterface
public interface EventSubscriber {

    void onReceiveEvent(Event event);

}

EventHandleThread

package eventbus;


import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public enum EventHandleThread {

    POST(Runnable::run),

    MAIN(new Executor() {
        private final Handler handler = new Handler(Looper.getMainLooper());

        @Override
        public void execute(Runnable command) {
            handler.post(command);
        }
    }),
    BACKGROUND(Executors.newCachedThreadPool(new ThreadFactory() {
        private final ThreadFactory threadFactory = Executors.defaultThreadFactory();

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = threadFactory.newThread(r);
            thread.setDaemon(true);
            return thread;
        }
    })), IO(Executors.newSingleThreadExecutor());


    private final Executor executor;

    EventHandleThread(Executor executor) {
        this.executor = executor;
    }


    public void submit(Runnable runnable) {
        executor.execute(runnable);
    }
}

EventBus

package eventbus;

import java.util.ArrayDeque;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public final class EventBus {

    private final Map<Class<?>, List<EventSubscriber>> mEventMap = new ConcurrentHashMap<>();
    private final Map<Object, EventFilter> mFilterMap = new ConcurrentHashMap<>();
    private final Map<Class<?>, Event> mStickyMap = new ConcurrentHashMap<>();
    private final Queue<Object> mPostingQueue = new ArrayDeque<>();
    private volatile boolean mPosting;
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    private static class InstanceHolder {
        private final static EventBus INSTANCE = new EventBus();
    }

    private EventBus() {

    }

    public static EventBus getInstance() {
        return InstanceHolder.INSTANCE;
    }


    public void register(EventSubscriber obj, EventFilter eventFilter) {
        assert obj != null;
        assert eventFilter != null;
        mFilterMap.put(obj, eventFilter);
        for (Class<?> event : eventFilter.getEventList()) {
            List<EventSubscriber> objList = mEventMap.computeIfAbsent(event, it -> new CopyOnWriteArrayList<>());
            objList.remove(obj);
            objList.add(obj);
            if (eventFilter.isStickyEvent(event)) {
                //do some things
                Event stickyEvent = mStickyMap.get(event);
                if (stickyEvent == null) {
                    continue;
                }
                eventFilter.getEventHandleThread(event).submit(() -> obj.onReceiveEvent(stickyEvent));
            }
        }
    }


    public void unregister(EventSubscriber obj) {
        assert obj != null;
        EventFilter eventFilter = mFilterMap.remove(obj);
        if (eventFilter != null) {
            List<Class<?>> eventList = eventFilter.getEventList();
            for (Class<?> event : eventList) {
                List<EventSubscriber> list = mEventMap.get(event);
                if (list != null) {
                    list.remove(obj);
                }
            }
        }

    }

    public void post(Object event) {
        post(event, false);
    }

    public void removeSticky(Class<?> event) {
        mStickyMap.remove(event);
    }

    public void post(Object event, boolean sticky) {
        assert event != null;

        Class<?> eventClass = event.getClass();
        if (sticky) {
            mStickyMap.put(eventClass, wrapEvent(event));
        }
        List<EventSubscriber> objectList = mEventMap.get(eventClass);
        if (objectList == null) {
            //dead event
            //post(NoSubScribeEvent(event));
            return;
        }
        try {
            readWriteLock.writeLock().lock();
            mPostingQueue.offer(event);

        } finally {
            readWriteLock.writeLock().unlock();
        }
        if (mPosting) {
            return;
        }
        mPosting = true;
        boolean empty = mPostingQueue.isEmpty();
        while (!empty) {
            Object object;
            try {
                readWriteLock.readLock().lock();
                object = mPostingQueue.poll();
                empty = mPostingQueue.isEmpty();
            } finally {
                readWriteLock.readLock().unlock();
            }
            if (object == null) {
                    break;
            }
            //publish
            Event wrapEvent = wrapEvent(object);
            for (EventSubscriber subscriber : objectList) {
                EventFilter eventFilter = mFilterMap.get(subscriber);
                assert eventFilter != null;
                eventFilter.getEventHandleThread(eventClass).submit(() -> subscriber.onReceiveEvent(wrapEvent));
            }
        }
        mPosting = false;
    }

    private Event wrapEvent(Object object) {
        Event event = new Event();
        event.payload = object;
        event.type = object.getClass();
        return event;
    }
}

Test

package eventbus;

public class Test implements EventSubscriber {

    public static void main(String[] args) throws InterruptedException {
        EventFilter eventFilter = new EventFilter();
        eventFilter.addEvent(String.class, EventHandleThread.BACKGROUND, true, 0);
        EventBus.getInstance().register(new Test(), eventFilter);
        for (int i = 0; i < 100; i++) {
            EventBus.getInstance().post("this is event" + i);
        }

        Thread.sleep(2000);
    }

    @Override
    public void onReceiveEvent(Event event) {
        System.out.println(event.type + " " + event.payload + " Thread=" + Thread.currentThread());
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值