仿EventBus做一个简单的基于订阅发布的事件总线
一、概述
在上一节分析了EventBus的源代码。本节仿照EventBus自己做一个事件总线框架,去掉了一些代码,只保留了核心框架,旨在梳理框架的框架原理,加深对框架流程的理解。
在贴出源代码之前先把这个自定义的EventBus的功能介绍一下。核心流程还是三个,注册、发布、取消注册(未写AnnotationProcessor部分,如果有需要大家可以自己实现),源码中的类绝大部分分仿照了EventBus原有的类名,只是类里面的内容做了简化调整。如:EventBus类只抽离了、post、register、unregister、subscribe方法等。
下面贴出library结构
自己实现的框架其用法和EventBus还是一样的,只是不能发送黏性事假和定义事件的优先级,使用实例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | public class MainActivity extends AppCompatActivity { private Button btn; private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); /** * 注册观察者对象 */ EventBus.getDefault().register( this ); btn = findViewById(R.id.main_btn); tv = findViewById(R.id.main_tv); btn.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { toPost(); } }); } /** * 用于接收post发送过来的事件 * * @param event 具体的事件主体 */ @Subscribe public void onMessageEvent(MessageEvent event) { switch (event.getArg1()) { case 0 : tv.setText( "接收小消息为:" + event.getMsg()); break ; } } @Override protected void onDestroy() { super .onDestroy(); /** * 取消订阅 */ EventBus.getDefault().unRegister( this ); } /** * 发送消息 */ public void toPost() { MessageEvent event = new MessageEvent(); event.setArg1( 0 ); event.setMsg( "杨洛峋小宝宝来了" ); EventBus.getDefault().post(event); } } |
二、源码介绍以及源码截图
下面提出关键源代码
EventBus.getDefault().register(subscriber)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | /** * 注册 * 订阅方法 * * @param subscriber */ public void register(Object subscriber) { Class clazz = subscriber.getClass(); List<SubscriberMethod> subscriberMethods = SubscriberMethodFinder.findSubscriberMethods(clazz); if (subscriberMethods != null && subscriberMethods.size() > 0 ) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } } /** * 真正的订阅 * * @param subscriber * @param subscriberMethod */ private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { Class eventType = subscriberMethod.getEventType(); Subscription newSubscription = new Subscription(subscriber, subscriberMethod); CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); if (subscriptions == null ) { subscriptions = new CopyOnWriteArrayList<>(); subscriptionsByEventType.put(eventType, subscriptions); } int size = subscriptions.size(); for ( int i = 0 ; i <= size; i++) { if (i == size) { //向订阅集合中加入订阅 subscriptions.add(i, newSubscription); break ; } } List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); if (subscribedEvents == null ) { subscribedEvents = new ArrayList<>(); typesBySubscriber.put(subscriber, subscribedEvents); } subscribedEvents.add(eventType); } |
EventBus.getDefault().post(eventObj)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | /** * 发送事件方法 * * @param eventObj */ public void post(Object eventObj) { PostingThreadState postingThreadState = currentPostingThreadState.get(); List<Object> queue = postingThreadState.getEventQueue(); queue.add(eventObj); postingThreadState.setMainThread(isMainThread()); //缺少一个发送事件的线程 Class<?> clazz = eventObj.getClass(); List<Subscription> subscriptions = subscriptionsByEventType.get(clazz); if (subscriptions != null ) { //循环遍历Subscriptions, for (Subscription subscription : subscriptions) { //分发事件 postToSubscription(subscription, eventObj, postingThreadState); } } } /** * 返回true为主线程,否则为子线程 * * @return */ private boolean isMainThread() { return Looper.myLooper() == Looper.getMainLooper(); } /** * 对事件进行分发 * * @param subscription * @param eventTypeObj */ private void postToSubscription(Subscription subscription, Object eventTypeObj, PostingThreadState postingThreadState) { switch (subscription.getSubscriberMethod().getThreadMode()) { case POSITION: invokeSubscriber(subscription, eventTypeObj); break ; case MAIN: if (isMainThread()) { invokeSubscriber(subscription, eventTypeObj); } else { mainThreadHandlerPoster.enqueue(subscription, eventTypeObj); } break ; case MAIN_ORDERD: if (mainThreadHandlerPoster != null ) { mainThreadHandlerPoster.enqueue(subscription, eventTypeObj); } else { invokeSubscriber(subscription, eventTypeObj); } break ; case BACKGROUND: if (backgroundPoster != null ) { backgroundPoster.enqueue(subscription, eventTypeObj); } else { invokeSubscriber(subscription, eventTypeObj); } break ; case ASYNC: asyncPoster.enqueue(subscription, eventTypeObj); break ; } } /** * 执行事件的具体方法 * * @param subscription * @param eventTypeObj */ public void invokeSubscriber(Subscription subscription, Object eventTypeObj) { try { subscription.getSubscriberMethod().getMethod().setAccessible( true ); subscription.getSubscriberMethod().getMethod().invoke(subscription.getSubscriber(), eventTypeObj); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } |
EventBus.getDefault().unRegister(subscriber)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /** * 取消注册方法 * 取消注册其实就清除两个 集合就行了,一个是Subscriptions集合,一个是typesBySubscriber集合 * * @param subscriber */ public void unRegister(Object subscriber) { List<Class<?>> eventTypes = typesBySubscriber.get(subscriber); if (eventTypes != null ) { for (Class<?> clazz : eventTypes) { CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(clazz); for (Subscription subscription : subscriptions) { if (subscriber == subscription.getSubscriber()) { subscriptions.remove(subscription); } } } typesBySubscriber.remove(subscriber); } } |
完整的源代码我发布到了github上欢迎大家下载,地址如下:myeventbuslib
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探