EventBus编程思想-实现简易版EventBus
Posted 0 and 1
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EventBus编程思想-实现简易版EventBus相关的知识,希望对你有一定的参考价值。
这是编程思想系列的第二篇,这里挑选用的比较多的EventBus讲解一下,不纠结实现细节。先理解设计思想,这也是研究学习开源框架的第一步。
代码:点击GitHub
一个开源框架的出现肯定是有原因的,便捷,性能基本离不开这两点。而EventBus设计的初衷应该是为了便捷。回想自己为什么要使用EventBus就明白了。简单提两句
组件通信方式
Intent:使用它实现组件跳转,并且能携带参数,但只能携带少量数据,同时在跨组件通信时局限性比较大
Handler:使用Handler进行组件通信耦合严重,容易造成内存泄漏
Broadcast:使用广播进行组件通信效率不高,安全性不高
Interface:不能跨线程通信
aidl:使用成本高
看了上面这些,是不是就明白了为什么会有EventBus的原因了吧。
设计框架的过程像是做一个产品。你首先要想一下设计这个框架的目的是什么?需求在哪里?存在即合理,
抛开源码,我们先想一想?如果要做一个组件之间通信工具,如何设计?。既然是为了方便,那么我们想怎么使用它呢?想让它有什么功能。可能会想到下面三点,(也可能想不到,哈哈),能想到的都是好的产品经理。。。
(1)在任意地方,发射数据。
(2)在任意地方,接收数据。
(3)可以指定接收数据的线程。
这个过程,类似产品经理根据需求设计产品的过程。然后我们根据图纸去实现。给我们的产品取一个名字吧:EventBus,其实我更喜欢叫DataBus,名字而已。萝卜青菜,各有所爱。想法有了,如何实现呢?操作起来,是不是可以运用设计模式中的观察者模式。
观察者(订阅者):任意类下的任意方法
被观察者(被订阅者):数据(任意类发射的数据)
实现思路
通过注解形式,标记观察者,然后被观察者提供订阅方法,通过注解扫描,把所有的观察者注入到容器中。被观察者提供发射数据的方法,遍历容器找到接收数据的方法(通过参数类型区分),然后通过反射方式调用观察者的方法。这就实现了被观察者发射数据通知观察者的需求。
代码实现
定义注解:Subscribe
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe
ThreadMode threadMode() default ThreadMode.POSTING;
定义EventBus 的行为:IBus。其实就是约定用户如何使用
public interface IBus
/**
* 订阅
*/
void register(Object subscriber);
/**
* 取消订阅,防止内存泄露
*/
void unRegister(Object subscriber);
/**
* 发射数据
*
* @param data
*/
void send(Object data);
订阅者对象模型:SubscriptionMethod
public class SubscriptionMethod
private Method method;
private Class<?> type;
private ThreadMode threadMode;
public Method getMethod()
return method;
public void setMethod(Method method)
this.method = method;
public Class<?> getType()
return type;
public void setType(Class<?> type)
this.type = type;
public ThreadMode getThreadMode()
return threadMode;
public void setThreadMode(ThreadMode threadMode)
this.threadMode = threadMode;
线程模型枚举
public enum ThreadMode
POSTING,
MAIN,
BACKGROUND,
ASYNC
被观察者:EventBus
mSubcription:保存观察者的容器
eventBusHelper 帮助类,做注解扫描,线程管理。 类的职责不能过重(遵循单一职责原则)
public class EventBus implements IBus
/**
* 订阅方法
* key 订阅类
* value 类中的订阅方法集合
*/
private final Map<Object, List> mSubcription;
private final EventBusHelper eventBusHelper;
private EventBus()
eventBusHelper = new EventBusHelper();
this.mSubcription = new HashMap<>();
private static class Instance
private static final EventBus instance = new EventBus();
public static EventBus getInstance()
return Instance.instance;
@Override
public void register(Object subscriber)
List<SubscriptionMethod> methodList = mSubcription.get(subscriber);
if (methodList == null || methodList.size() == 0)
methodList = new ArrayList<>();
Class<?> clazz = subscriber.getClass();
while (clazz != null)
String className = clazz.getName();
if (className.startsWith("java.") || className.startsWith("javax.") || className.startsWith("android."))
break;
eventBusHelper.findAnnotationMethod(methodList, clazz);
//查找父类,有继承关系
clazz = clazz.getSuperclass();
mSubcription.put(subscriber, methodList);
@Override
public void send(Object event)
if (mSubcription.size() == 0)
return;
Set<Object> set = mSubcription.keySet();
Iterator<Object> iterable = set.iterator();
while (iterable.hasNext())
Object next = iterable.next();
List<SubscriptionMethod> methodList = mSubcription.get(next);
if (methodList == null)
continue;
int size = methodList.size();
for (int i = 0; i < size; i++)
SubscriptionMethod method = methodList.get(i);
//method.getType()是获取方法参数类型,这里是判断发布的对象类型是否与订阅方法的参数类型一致
if (method.getType().isAssignableFrom(event.getClass()))
eventBusHelper.invokeWithThread(next, method, event);
@Override
public void unRegister(Object target)
List<SubscriptionMethod> methodList = mSubcription.get(target);
if (methodList == null) return;
methodList.clear();
mSubcription.remove(target);
帮助类:EventBusHelper,把注解扫描的代码抽离出来,避免被观察者EventBus职责过重,遵循单一职责的原则
class EventBusHelper
private final Handler mHandler;
private final ExecutorService mExecutorService;
public EventBusHelper()
this.mHandler = new Handler(Looper.getMainLooper());
this.mExecutorService = Executors.newCachedThreadPool();
public void findAnnotationMethod(List<SubscriptionMethod> methodList, Class<?> clazz)
Method[] m = clazz.getDeclaredMethods();
int size = m.length;
for (int i = 0; i < size; i++)
Method method = m[i];
Subscribe annotation = method.getAnnotation(Subscribe.class);
if (annotation == null)
continue;
Type genericReturnType = method.getGenericReturnType();
if (!"void".equals(genericReturnType.toString()))
throw new EventBusException("方法返回值必须是void");
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 1)
throw new EventBusException("方法修饰符必须是public,且是非静态,非抽象");
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != 1)
throw new EventBusException("方法参数个数必须是一个");
//这里就需要实例化订阅方法对象了
SubscriptionMethod subscriptionMethod = new SubscriptionMethod();
subscriptionMethod.setMethod(method);
subscriptionMethod.setType(parameterTypes[0]);
subscriptionMethod.setThreadMode(annotation.threadMode());
methodList.add(subscriptionMethod);
public void invokeWithThread(final Object next, final SubscriptionMethod method, final Object event)
//进行线程切换
switch (method.getThreadMode())
case POSTING:
invoke(next, method, event);
break;
case MAIN:
//通过Looper判断当前线程是否是主线程
//也可以通过线程名判断 "main".equals(Thread.currentThread().getName())
if (Looper.getMainLooper() == Looper.myLooper())
invoke(next, method, event);
else
mHandler.post(new Runnable()
@Override
public void run()
invoke(next, method, event);
);
break;
case BACKGROUND:
if (Looper.getMainLooper() == Looper.myLooper())
mExecutorService.execute(new Runnable()
@Override
public void run()
invoke(next, method, event);
);
else
invoke(next, method, event);
break;
case ASYNC:
mExecutorService.execute(new Runnable()
@Override
public void run()
invoke(next, method, event);
);
break;
private void invoke(Object next, SubscriptionMethod method, Object event)
Method m = method.getMethod();
try
m.invoke(next, event);
catch (IllegalAccessException e)
e.printStackTrace();
catch (InvocationTargetException e)
e.printStackTrace();
大功告成,开始使用
public class MainActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
EventBus.getInstance().register(this);
setContentView(R.layout.activity_main);
new Thread(new Runnable()
@Override
public void run()
EventBus.getInstance().send(new Message(1, "activity启动了"));
).start();
@Subscribe(threadMode = ThreadMode.MAIN)
public void eventBus(Message message)
Log.i("event_bus", message.getMsg());
@Override
protected void onDestroy()
super.onDestroy();
EventBus.getInstance().unRegister(this);
以上如果有理解不对,或者代码错误的地方,请大神在评论区留言,谢谢!
以上是关于EventBus编程思想-实现简易版EventBus的主要内容,如果未能解决你的问题,请参考以下文章
RxJava编程思想3-(实现简易版Rxjava,如何实现线程切换)
RxJava编程思想2-(实现简易版Rxjava,如何实现操作符?)
RxJava编程思想2-(实现简易版Rxjava,如何实现操作符?)