EventBus原理

现在我们实现的一个效果是,有两个acivity如图

MainActivity:当点击"click"按钮的时候,就会跳转到secondActivity,而secondActivity里面有两个按钮,点击以后,可以改变MainActivity里面TextView的文字

EventBus原理_第1张图片dEventBus原理_第2张图片

改变以后如图:

EventBus原理_第3张图片EventBus原理_第4张图片



这其实就是一个消息传递的过程,假设我们用EventBus,下面我们来看一下,代码是怎么写的

[java]  view plain  copy
  1. public class MainActivity extends FragmentActivity {  
  2.   
  3.     Button btn;  
  4.     TextView text;  
  5.     @Override  
  6.     protected void onCreate(Bundle savedInstanceState) {  
  7.         super.onCreate(savedInstanceState);  
  8.         setContentView(R.layout.activity_main);  
  9.         EventBus.getInstance().register(this);  
  10.         btn = (Button) findViewById(R.id.btn);  
  11.         text = (TextView) findViewById(R.id.text);  
  12.         btn.setOnClickListener(new View.OnClickListener() {  
  13.             @Override  
  14.             public void onClick(View v) {  
  15.                 startActivity(new Intent(MainActivity.this,SecondActivity.class));  
  16.             }  
  17.         });  
  18.     }  
  19.   
  20.     public void onEvent(Info i){  
  21.         Log.i("cky", i.msg);  
  22.     }  
  23.   
  24.     public void onEventMain(Info i){  
  25.         text.setText(i.msg);  
  26.     }  
  27.   
  28.     public void onEventMain(Info2 i){  
  29.         text.setText(text.getText()+i.msg);  
  30.     }  
  31. }  

可以看到,我们在onCreate()里面调用了EventBus.register(this),来注册事件

然后又两者函数,一个是onEvent(),这个方法里面的代码,会在一个子线程中执行

一个是onEventMain(),这个方法里面的代码,会在UI线程执行。在这里,我们改变了TextView中的文字。


使用过EventBus的朋友,现在看来使用方式是不是跟EventBus很像(我就是想实现EventBus的效果啊)。EventBus对事件在不同线程中处理,有四种方式,但是我们这里只写了简单的两者,主线程和非主线程,这样代码更加简单,而且原理是一样的,会写两个,就会写多个。


然后SecondActivity里面,大家一定知道是怎么回事,肯定是在点击里面调用了post()方法

[java]  view plain  copy
  1. public class SecondActivity extends Activity {  
  2.   
  3.     Button btn2;  
  4.     Button btn3;  
  5.     @Override  
  6.     protected void onCreate(Bundle savedInstanceState) {  
  7.         super.onCreate(savedInstanceState);  
  8.         setContentView(R.layout.activity_second);  
  9.         btn2 = (Button) findViewById(R.id.btn2);  
  10.         btn2.setOnClickListener(new View.OnClickListener() {  
  11.             @Override  
  12.             public void onClick(View v) {  
  13.                 EventBus.getInstance().post(new Info("信息1"));  
  14.             }  
  15.         });  
  16.         btn3 = (Button) findViewById(R.id.btn3);  
  17.         btn3.setOnClickListener(new View.OnClickListener() {  
  18.             @Override  
  19.             public void onClick(View v) {  
  20.                 EventBus.getInstance().post(new Info2("信息2"));  
  21.             }  
  22.         });  
  23.     }  
  24. }  

就是这么简单,使用方法跟EventBus如出一辙。

大家可能会好奇到底怎么样用简单的代码,实现这样的效果,下面我带大家看我的代码。!


先看EventBus

[java]  view plain  copy
  1. public class EventBus {  
  2.     HashMap<Class<?>,ArrayList<Subscription>> subscriptionsByEventType =  
  3.             new HashMap<Class<?>,ArrayList<Subscription>>();  
  4.     MainThreadHandler mainThreadHandler = new MainThreadHandler(this,Looper.getMainLooper());  
  5.     AsyThreadHandler asyThreadHandler = new AsyThreadHandler(this);  
  6.   
  7.     private final static EventBus instance = new EventBus();  
  8.     public static EventBus getInstance(){  
  9.            return instance;  
  10.     }  
  11.     private EventBus(){};  
  12. }  

有几个地方:

1,将构造函数设为私有,使用了getInstance()来给外界提供EventBus实例,其实就是一个单例模式

2,有几个复杂的属性,首先是subscriptionsByEventType,这是一个map,key代表某个bean类。

使用过EventBus都知道,我们在传递信息的时候,要传递一个实体类,例如我上面的例子,就是Info这个类。

value值是一个ArrayList<Subscription>,Subscription的意义就是一个订阅,是一个我创建的实体类。这个类的具体含义留到后面讲。

剩下两个mainThreadHandler和asyThreadHandler,顾名思义就是分别用来处理主线程和子线程的,都是我创建的类。


看到这里可能大家有点糊涂,出现了三个类,别急,我一定会解释清楚。现在先让我们来看非常重要的register()方法。

[java]  view plain  copy
  1. public void register(Object subscriber){  
  2.         Class<?> clazz = subscriber.getClass();//获取订阅者的类型  
  3.         Method[] methods = clazz.getMethods();//获取该类的所有方法  
  4.         for(Method m:methods){//遍历方法  
  5.             String name = m.getName();//获取方法名  
  6.             if(name.startsWith("onEvent")){//判断方法名是否以"onEvent"开头             </span>  
  7.                 Class<?>[] params = m.getParameterTypes();//获取该方法的参数类型  
  8.                 ArrayList<Subscription> arr;  
  9.                 if(subscriptionsByEventType.containsKey(params[0])){  
  10.                     arr = subscriptionsByEventType.get(params[0]);  
  11.                 }else{  
  12.                     arr = new ArrayList<Subscription>();  
  13.                 }  
  14.                 int len = name.substring("onEvent".length()).length();//截取方法除"onEvent"部分    
  15.                 Subscription sub;  
  16.                 if(len==0){//如果剩余长度为0,说明是子线程中执行  
  17.                     sub =  new Subscription(subscriber,new SubscriberMethod(m,params[0],0));  
  18.                 }else{//否则,在主线程执行  
  19.                     sub =  new Subscription(subscriber,new SubscriberMethod(m,params[0],1));  
  20.                 }  
  21.                 arr.add(sub);  
  22.                 subscriptionsByEventType.put(params[0],arr);  
  23.             }  
  24.         }  
  25.     }  

从上面的注释我们可以看到,我们先获取了订阅者(例子中是MainActivity)的方法,找到onEvent开头的方法,获得它们的参数类型

然后判断subscriptionsByEventType是否有以这些参数类型为key的数据,如果没有,新建一个ArrayList<Subscription>。

然后我们先看Subscription

[java]  view plain  copy
  1. public class Subscription {  
  2.     Object subscriber;  
  3.     SubscriberMethod SubscriberMethod;  
  4.     public Subscription(Object subscriber, SubscriberMethod SubscriberMethod) {  
  5.         this.subscriber = subscriber;  
  6.         this.SubscriberMethod = SubscriberMethod;  
  7.     }  
  8. }  
它代表一个订阅,拥有subsriber,也就是订阅者

还有一个SubscriberMethod,这是订阅方法类

[java]  view plain  copy
  1. public class SubscriberMethod {  
  2.     Method m;  
  3.     int type;  
  4.     public SubscriberMethod(Method m, Class<?> param, int type) {  
  5.         this.m = m;  
  6.         this.type = type;  
  7.     }  
  8. }  
它有一个Method m属性,就是我们注册的方法,另外一个type就是标记,0代表m在子线程中执行,其余代表在主线程中执行。


所以总的来说register()通过遍历订阅者,找到订阅方法(onEvent(),onEventMain()),将方法包装成SubscriberMethod类,再将SubscriberMethod和订阅者一起,

包装成Subscription类。

而subscriptionsByEventType根据参数类型为key,所谓参数类型,在例子中就是Info.class和info2.class


为什么我们要用参数类型为键呢?我们来想,以后我们在post的时候,是这样调用post的:

[java]  view plain  copy
  1. EventBus.getINstance().post(new Info("信息"));  
我们要传进去一个参数,而EventBus的任务就是,调用所有注册了这个参数类型的订阅方法。

所有显然,我们要根据参数类型,找到这些方法,这是key是参数类型的原因了。


Ok,register()方法下来,最重要的一件事就是我们得到了subscriptionsByEventType


接下来我们就可以看post方法了

[java]  view plain  copy
  1. public void post(Object event){  
  2.         Class<?> clazz = event.getClass();  
  3.         ArrayList<Subscription> arr = subscriptionsByEventType.get(clazz);  
  4.         for(Subscription sub:arr){//遍历订阅  
  5.             if(sub.SubscriberMethod.type==0){  
  6.                 asyThreadHandler.post(sub,event);  
  7.             }else{  
  8.                 mainThreadHandler.post(sub,event);  
  9.             }  
  10.         }  
  11.     }  

post()方法里面,如同我们上面所说,获取了参数类型,然后在subscriptionsByEventType中查询所有改类型对应的订阅Subscription

对于Subscription,它有我们订阅类的所有信息。

首先根据type判断是在主线程还是子线程执行,然后调用一开始讲到的两个类的实例就好了。


我们先看asyThreadHandler,这个会在子线程执行

[java]  view plain  copy
  1. public class AsyThreadHandler {  
  2.     private EventBus eventBus;  
  3.     AsyThreadHandler(EventBus eventBus) {  
  4.         this.eventBus = eventBus;  
  5.     }  
  6.   
  7.     public void post(final Subscription sub, final Object event){  
  8.         new Thread(){  
  9.             @Override  
  10.             public void run() {  
  11.                 eventBus.invoke(sub,event,sub.SubscriberMethod.m);  
  12.             }  
  13.         }.start();  
  14.     }  
  15. }  
代码很简单,其实它的post方法,就是启用了一个线程,然后在线程里面,调用了EventBus的invoke()方法。

我们看这个方法

[java]  view plain  copy
  1. public void invoke(final Subscription sub, final Object event,Method m){  
  2.         try {  
  3.             m.invoke(sub.subscriber,event);  
  4.         } catch (IllegalAccessException e) {  
  5.             e.printStackTrace();  
  6.         } catch (InvocationTargetException e) {  
  7.             e.printStackTrace();  
  8.         }  
  9.     }  

其实只有一句话,就是调用了反射去执行方法。m是订阅方法,sub.subscriber就是订阅者,event就是post()方法传入的实体

这样我们就在子线程中调用了这个方法了,相当于MainActivity主动调用这个方法。


那么在主线程中呢,大同小异

[java]  view plain  copy
  1. public class MainThreadHandler extends Handler{  
  2.     private final EventBus eventBus;  
  3.   
  4.     MainThreadHandler(EventBus eventBus, Looper looper) {  
  5.         super(looper);  
  6.         this.eventBus = eventBus;  
  7.     }  
  8.   
  9.     @Override  
  10.     public void handleMessage(Message msg) {  
  11.         EventBus.getInstance().invoke(sub,event,sub.SubscriberMethod.m);  
  12.     }  
  13.   
  14.     Subscription sub;  
  15.     Object event;  
  16.     public void post(Subscription sub, Object event){  
  17.         this.sub = sub;  
  18.         this.event = event;  
  19.         sendEmptyMessage(0);  
  20.     }  
  21. }  
可以看到继承了handler,表明在主线程中调用。

post方法传递了一个空message,在handlerMessage()方法里面,又是调用了EventBus的invoke()方法。


殊途同归,最后都是调用EventBus的invoke()方法,不过一个在子线程中调用,一个在主线程中调用。

就这样,我们轻而易举得实现了EventBus的基本功能哦!

是不是觉得代码很简单,其实上面的代码,已经将EventBus框架的思路将清楚了,大家明白这思路,再看EventBus就容易多了。


通过学习,我们也注意到,EventBus(我是这github上面的)存在缺点

1,就是订阅方法名固定,就像我上面,只能用onEvent(),onEventMain()方法,使用者不能自己规定方法名

2,根据参数类型来调用方法,也就是post(Info i),所有的订阅了Info类的订阅方法都会被调用。很多时候我们不希望这样,而是希望post给特定的订阅方法。





流程图

先来一张整体流程图:


思路图

在看看register 和 post 的过程思路图:

register

post

通过上面的几张图,我们可以大致了解eventbus的工作流程,下面我们在来介绍一下这个流程中比较重要的几个方法



你可能感兴趣的:(EventBus原理)