一.EventBus是什么
EventBus是一款Android下的发布/订阅事件总线机制。可以代替Intent、Handler、Broadcast等在Fragment、Activity之间传递消息。
优点:开销小,代码优雅。将发送者和接受者解耦。
二.为什么我们要写一套自己框架呢
既然官方的EventBus这么牛逼,为什么我们还要自己写一套呢,问得好,官方的EventBus虽然很牛逼,但是不一定每个功能我们都用得到啊,如果你在项目里引入想这些官方的框架,你的项目就会很臃肿,笨重。那么问题来了,如何实现EventBus的功能还不显得笨重呢,接下来就来看看吧。
三.如何封装
大家都知道eventBus有自己的订阅者,发布者,发布者发布消息,订阅者就都能接收到消息。
1.订阅者订阅事件
订阅者一般都是写一个方法,然后在标明自己的标签,发布者根据订阅者的标签发送到对应的订阅者身上。
那么如何给方法提供标注呢
其实较好的方法是注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe {
String[] value();
}
此注解表明在方法体上并且保留到运行时,定义一个数组标签,因为有的订阅者需要多个标签。
然后我们在给订阅者订阅事件
@Subscribe({"1"})
public void test1(String msg,String msg1){
Log.e("test==========","msg:"+msg+" msg1:"+msg1);
}
@Subscribe({"1","2"})
public void test2(String msg,Integer msg1){
Log.e("test==========","msg:"+msg+" msg1:"+msg1);
}
2.根据标签存储订阅者
第一步我们已经表明了哪些方法时订阅者,那我们要如何让发布者知道呢,我们可以把所有的订阅者根据标签存储到一个对象池,到时候发布者要发布消息得时候就可以从里面去找啦。
①.拿到调用者的所有订阅对象
/**
* 拿到订阅的方法
* @param object 调用者
* @return
*/
public List<SubscribeMethod> findSubscribeMethod(Object object) {
Class<?> subscribeClass = object.getClass();
List<SubscribeMethod> subscribeMethods = methodmap.get(subscribeClass);
if (subscribeMethods == null) {
subscribeMethods = new ArrayList<>();
//拿到里面的所有方法
Method[] declaredMethods = subscribeClass.getDeclaredMethods();
for (Method method : declaredMethods) {
//拿到注解
Subscribe subscribe = method.getAnnotation(Subscribe.class);
if (subscribe != null) {
String[] values = subscribe.value();
//拿到方法的参数类型集合
Class<?>[] parameterTypes = method.getParameterTypes();
for (String lable : values) {
method.setAccessible(true);
//一个标签就存储一个subscribeMethod
subscribeMethods.add(new SubscribeMethod(lable, method, parameterTypes));
}
}
}
methodmap.put(subscribeClass, subscribeMethods);
}
return subscribeMethods;
}
因为反射会耗费性能,所有我们就把对象存储在对象池里,下次就不用在反射
1.先去缓存区区找,如果找不到就去传进来的调用者去获取
2.根据调用者去获取所有的方法
3.遍历所有的方法体,找到注解的方法
4.拿到注解方法的value[]
5.最后和method,parameterType,lable组成一个对象存储到methodmap里
实际上拿到的是这样的:
②.根据标签存储订阅者
到这里methodmap就存储完成,虽然说订阅者的标签有很多相同的,但是相同的标签对应的是不同的订阅者,那我们就可以根据标签存储订阅者集合,存到subscriptionmap对象池里,把SubscribeMethod和Object(调用者)存到Subscription,到时候发布消息,各个订阅者都能接受到消息,如果不明白的可以看下图:
其中object是调用者,即方法对应的类对象,后面执行的时候会用到
/**
* 订阅
* @param object
*/
public void regesiter(Object object) {
if(resultmap.containsKey(object.getClass())){
return;
}
//取消订阅时用
List<String> lables = resultmap.get(object.getClass());
if (lables == null) {
lables = new ArrayList<>();
resultmap.put(object.getClass(), lables);
}
//找到所有的订阅者,一个标签对应一个订阅者,包含重复订阅者
List<SubscribeMethod> subscribeMethods = findSubscribeMethod(object);
for (SubscribeMethod subscribeMethod : subscribeMethods) {
String lable = subscribeMethod.getLable();
if (!lables.contains(lable)) {
lables.add(lable);
}
//一个标签对应所有Subscription对象,subscriptions里的都是相同的标签
List<Subscription> subscriptions = subscriptionmap.get(lable);
if (subscriptions == null) {
subscriptions = new ArrayList<>();
subscriptionmap.put(lable, subscriptions);
}
subscriptions.add(new Subscription(subscribeMethod, object));
}
}
一样的,也是保存到对象池里,if(resultmap.containsKey (object.getClass())){ return; }
这行代码是为了避免重复跳到这个页面的时候重复添加subscriptionmap对象池,methodmap就不用了,它是根据类进行区分,在findSubscribeMethod()就做好了判断。
3.发布事件
发布事件,实际上是拿到方法的调用者,在传入实际的参数,对应的订阅者才会响应,因为传入的参数各有不同,所有还需在加一层判断,具体实现见如下代码:
/**
* 发送消息给订阅者
*
* @param lable 标签
* @param params 实际参数数组
*/
public void post(String lable, Object... params) {
List<Subscription> subscriptions = subscriptionmap.get(lable);
if (null == subscriptions) {
return;
}
for (Subscription subscription :
subscriptions) {
SubscribeMethod subscribeMethod = subscription.getSubscribeMethod();
//调用者
Object object = subscription.getObject();
//方法
Method method = subscribeMethod.getMethod();
//参数类型数组
Class[] argumentType = subscribeMethod.getArgumentType();
Object[] realparams = new Object[argumentType.length];
if (params != null) {
for (int i = 0; i < argumentType.length; i++) {
//如果长度在params范围之内并且类型会对应则存储,否则存null
if (i < params.length && argumentType[i].isInstance(params[i])) {
realparams[i] = params[i];
} else {
realparams[i] = null;
}
}
}
try {
//调用方法
method.invoke(object, realparams);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
- 根据标签拿到SubScription集合
- 遍历Subscription集合,拿到SubscribeMethod,Object调用者,在拿到method
- 传进来的参数如果不是对应的类型,或者数组的长度不是正确的长度,则给他赋空
Object[] realparams = new Object[argumentType.length];
if (params != null) {
for (int i = 0; i < argumentType.length; i++) {
if (i < params.length && argumentType[i].isInstance(params[i])) {
realparams[i] = params[i];
} else {
realparams[i] = null;
}
}
}
4.最后传入参数调用方法
method.invoke(object, realparams);
4.注销
有订阅当然要有取消订阅,我们可以在订阅的时候根据类保存所有的标签,然后在取消订阅的时候根据类拿到对应的标签集合,遍历标签集合,把所有含有该标签的订阅者给移除。如下图所示:
/**
* 取消订阅
*/
public void unregister(Object object) {
List<String> lables = resultmap.get(object.getClass());
if (lables != null) {
for (String lable : lables) {
List<Subscription> subscriptions = subscriptionmap.get(lable);
Iterator<Subscription> iterator = subscriptions.iterator();
while (iterator.hasNext()){
Subscription subscription = iterator.next();
Object object1 = subscription.getObject();
if(object1==object){
iterator.remove();
}
}
}
if(resultmap.containsKey(object.getClass())){
resultmap.remove(object.getClass());
}
}
}}
四.框架的使用
package com.example.myeventbus;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.example.myeventbus.bus.MyBus;
import com.example.myeventbus.bus.Subscribe;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//订阅
MyBus.getInstance().regesiter(this);
}
@Subscribe({"1"})
public void test1(String msg,String msg1){
Log.e("main==test===","msg:"+msg+" msg1:"+msg1);
}
@Subscribe({"1","2"})
public void test2(String msg,Integer msg1){
Log.e("main==test===","msg:"+msg+" msg1:"+msg1);
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消订阅
MyBus.getInstance().unregister(this);
}
public void skip(View view) {
startActivity(new Intent(this,Main2Activity.class));
}
public void traverse(View view) {
MyBus.getInstance().traverse();
}
}
package com.example.myeventbus;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.example.myeventbus.bus.MyBus;
import com.example.myeventbus.bus.Subscribe;
public class Main2Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
MyBus.getInstance().regesiter(this);
//发布事件
MyBus.getInstance().post("1","1",2);
}
@Subscribe("1")
public void test(String msg,String msg1,String msg2){
Log.e("sencod==test===","msg:"+msg+" msg1:"+msg1+" msg2:"+msg2);
}
public void skip(View view) {
startActivity(new Intent(this,MainActivity.class));
}
@Override
protected void onDestroy() {
super.onDestroy();
MyBus.getInstance().unregister(this);
}
public void traverse(View view) {
MyBus.getInstance().traverse();
}
}
到这里就结束了,大家看看是不是很简单。
最后附上源码