Interviewer: Can you write EventBus by hand?

EventBus believes that many people are familiar with it. Although Google has officially released JetPack to replace it, some design ideas of EventBus are still worth learning. Let’s write a simple EventBus case below

In fact, the principle of EventBus is not difficult, it is to maintain several arrays, then find the corresponding registered object according to the corresponding key, and call the corresponding method by radiation.

There is a big difference before and after EventBus3.0. The biggest difference is that after 3.0, a reference object is generated during compiling through apt, which greatly improves performance.

The easiest to use

//注册事件
EventBus.getDefault().register(this);

//注册方法
@Subscribe
public void event(BaseEventBusBeaan message) {
  LogUtils.d("EventBusActivity event");
}

//发送事件
EventBus.getDefault().post(new BaseEventBusBeaan("123", new Bundle()));

//回收
EventBus.getDefault().unregister(this);

post process

First, we should clarify our needs, we need to be in postwhen an object out of all the registered listeners that the object class can receive this notification, then this should require an array to store data.

//post出去的对象为key,一个注册者Subscription的list作为value
private Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;

//这个Subscription包括下面参数
public class Subscription {

    final Object subscriber;  //activity或者fragment
    final SubscriberMethod subscriberMethod;  

    public Subscription(Object subscriber, SubscriberMethod subscriberMethod) {
        this.subscriber = subscriber;
        this.subscriberMethod = subscriberMethod;
    }
}

public class SubscriberMethod {

    private String methodName; // 订阅方法名
    private Method method; // 订阅方法,用于最后的自动执行订阅方法
    private ThreadMode threadMode; // 线程模式
    private Class<?> eventType; // 事件对象Class,如:UserInfo.class
}

With subscriptionsByEventTypethis, we can post()find all registrants according to the sent events, traverse listand reflect one by one.

public void post(Object event) {
  postSingleEventForEventType(event, event.getClass());
}

//遍历所有的订阅者,发送对应的事件
private void postSingleEventForEventType(Object event, Class<?> eventClass) {
  CopyOnWriteArrayList<Subscription> subscriptions;

  synchronized (this) {
    subscriptions = subscriptionsByEventType.get(eventClass);
  }
  if (subscriptions != null && !subscriptions.isEmpty()) {
    for (Subscription subscription : subscriptions) {
        invokeSubscriber(subscription, event);
    }
  }
}

//这里暂时不考虑线程的问题
private void invokeSubscriber(Subscription subscription, Object event) {
  try {
    subscription.subscriberMethod.getMethod().invoke(subscription.subscriber, event);
  } catch (Exception e) {
    e.printStackTrace();
  }
}

The above is a simplified version of the postprocess.

Register process

The above-mentioned difference is posta very key point, that is subscriptionsByEventTypethe source of the data, we should naturally think of it in registerthe process.

Look back at subscriptionsByEventTypethe key and value again, and find that most of these values ​​can be obtained from the following functions.

@Subscribe
public void event(BaseEventBusBeaan message) {
  LogUtils.d("EventBusActivity event");
}

So we need to traverse all the methods in the class, find all the @Subscribeannotated functions, and save them.

The apt scheme is adopted here . During the compilation process, it traverses all classes, finds all @Subscribeannotated functions, and saves them in a certain format. The result will generate classes similar to the following.

//具体的生成过程不再这里赘述,想要了解的可以自己看文末的代码
//编译过程中将所有 @Subscribe注释过的方法保存到SUBSCRIBER_INDEX数组中。
//key为函数所属的类,比如MainActivity,value则是一个对象,保存一个数据的集合。
public final class MyEventBusIndex implements SubscriberInfoIndex {
  private static final Map<Class, SubscriberInfo> SUBSCRIBER_INDEX;

  static {
    SUBSCRIBER_INDEX = new HashMap<Class,SubscriberInfo>();
    putIndex(new SimpleSubscriberInfo(EventBusActivity2.class,
            new SubscriberMethod[] {
                    new SubscriberMethod(EventBusActivity2.class, "event", BaseEventBusBeaan.class, ThreadMode.POSTING, 0, false),
                    new SubscriberMethod(EventBusActivity2.class, "sticky", UserInfo.class, ThreadMode.POSTING, 2, true),
                    new SubscriberMethod(EventBusActivity2.class, "sticky2", UserInfo.class, ThreadMode.POSTING, 2, true)}
                    ));
  }

  private static void putIndex(SubscriberInfo info) {
    SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
  }

  @Override
  public SubscriberInfo getSubscriberInfo(Class subscriberClass) {
    return SUBSCRIBER_INDEX.get(subscriberClass);
  }
}

With MyEventBusIndexAfter that, start registerthe process.

public void register(Object subscriber) {
  Class<?> subscriberClass = subscriber.getClass();
  List<SubscriberMethod> subscriberMethods = findSubscriberMethods(subscriberClass);
	
  //这个循环是生成subscriptionsByEventType对象的关键,
  for (SubscriberMethod method : subscriberMethods) {
    subscribe(subscriber, method);
  }
}

//1.根据subscriberClass先从methodBySubscriber缓存中找
private List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
  List<SubscriberMethod> subscriberMethods = methodBySubscriber.get(subscriberClass);
  if (subscriberMethods != null) return subscriberMethods;

  subscriberMethods = findByAPT(subscriberClass);
  if (subscriberMethods != null) {
    methodBySubscriber.put(subscriberClass, subscriberMethods);
  }

  return subscriberMethods;
}

//2.接着从subscriberInfoIndex查找,subscriberInfoIndex这个对象就是上文中提到的MyEventBusIndex的对象
private List<SubscriberMethod> findByAPT(Class<?> subscriberClass) {
  if (subscriberInfoIndex == null) {
    throw new RuntimeException("未添加索引文件");
  }
  SubscriberInfo subscriberInfo = subscriberInfoIndex.getSubscriberInfo(subscriberClass);
  if (subscriberInfo != null) return Arrays.asList(subscriberInfo.getSubscriberMethods());
  return null;
}

Then start traversal subscriberMethods(because each subscriber does not necessarily have only one method to add @Subscribeannotations)

for (SubscriberMethod method : subscriberMethods) {
  subscribe(subscriber, method);
}

//在这里就可以生成post过程中所需要的 subscriptionsByEventType 数据了。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
  Class<?> eventType = subscriberMethod.getEventType();
  CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
  if (subscriptions == null) {
    subscriptions = new CopyOnWriteArrayList<>();
    subscriptionsByEventType.put(eventType, subscriptions);
  }

  Subscription subscription = new Subscription(subscriber, subscriberMethod);
  subscriptions.add(i, subscription);

  //订阅者类型集合,unregister的时候用到
  List<Class<?>> subscribeEvents = typeBySubscriber.get(subscriber);
  if (subscribeEvents == null) {
    subscribeEvents = new ArrayList<>();
    typeBySubscriber.put(subscriber, subscribeEvents);
  }
  subscribeEvents.add(eventType);
}

At this point, in fact, a simple process has been passed.

Summarize the general process

  1. It will all be apt at compile time by @Subscribeadding annotation function to the MyEventBusIndexobject.
  2. Data registergenerated in the process subscriptionsByEventType.
  3. In postthrough the process subscriptionsByEventTypeto find the corresponding function data, and then reflected by the way call.

Priority issue

This problem is also very simple. You only need to make a priority judgment when inserting data.

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
  Class<?> eventType = subscriberMethod.getEventType();
  CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
  if (subscriptions == null) {
    subscriptions = new CopyOnWriteArrayList<>();
    subscriptionsByEventType.put(eventType, subscriptions);
  }

  Subscription subscription = new Subscription(subscriber, subscriberMethod);
  
  //根据优先级插队
  int size = subscriptions.size();
  for (int i = 0; i <= size; i++) {
    if (i == size || subscriberMethod.getPriority() > subscriptions.get(i).subscriberMethod.getPriority()) {
      if (!subscriptions.contains(subscription)) subscriptions.add(i, subscription);
      break;
    }
  }

  //订阅者类型集合,unregister的时候用到
  List<Class<?>> subscribeEvents = typeBySubscriber.get(subscriber);
  if (subscribeEvents == null) {
    subscribeEvents = new ArrayList<>();
    typeBySubscriber.put(subscriber, subscribeEvents);
  }
  subscribeEvents.add(eventType);
}

Sticky event

Ordinary events are registered first, then sent. On the contrary, sticky events are sent first and then registered.

We only need to change the order. Store the event when sending, and then registercheck if there is a suitable event when

public void postSticky(Object event) {
  stickyEvents.put(event.getClass(), event);
}

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
		....
    
    //检查是否有合适的事件可以触发
    sticky(subscriberMethod, eventType, subscription);
}

private void sticky(SubscriberMethod subscriberMethod, Class<?> eventType, Subscription subscription) {
  if (subscriberMethod.isSticky()) {
    Object event = stickyEvents.get(eventType);
    if (event != null) {
      postToSubscription(subscription, event);
    }
  }
}

The last postToSubscriptioncode added.

private void postToSubscription(final Subscription subscription, final Object event) {
  switch (subscription.subscriberMethod.getThreadMode()) {
    case POSTING: // 订阅、发布在同一线程
      invokeSubscriber(subscription, event);
      break;
    case MAIN:
      //事件发送方是主线程
      if (Looper.myLooper() == Looper.getMainLooper()) {
        invokeSubscriber(subscription, event);
      } else {
        //事件发送方是子线程
        handler.post(new Runnable() {
          @Override
          public void run() {
            invokeSubscriber(subscription, event);
          }
        });
      }
      break;
    case ASYNC:
      //发送方在主线程
      if (Looper.myLooper() == Looper.getMainLooper()) {
        executorService.execute(new Runnable() {
          @Override
          public void run() {
            invokeSubscriber(subscription, event);
          }
        });
      } else {
        invokeSubscriber(subscription, event);
      }
      break;
  }
}

private void invokeSubscriber(Subscription subscription, Object event) {
  try {
    subscription.subscriberMethod.getMethod().invoke(subscription.subscriber, event);
  } catch (Exception e) {
    e.printStackTrace();
  }
}

 

Guess you like

Origin blog.csdn.net/qq_39477770/article/details/111475717