EventBus基础

823 阅读7分钟

一、EventBus简介

# EventBus is a publish/subscribe event bus for Android and Java.
# EventBus是一个事件发布/订阅框架。

在这里插入图片描述

二、基础用法

举例:在MainActivity中,在用户填写姓名、年龄后点击确定,需要将用户信息发送到给另一个Activity展示出来,EventBus实现方法如下:

2.1 添加依赖

dependencies {
    // eventbus
    implementation 'org.greenrobot:eventbus:3.1.1'
}

2.2 定义Event

首先,定义Event,即用户信息类如下:

public class User{
	public String name;
	public int age;
}

2.3 Event Subscribers

事件订阅方;分为注册、处理、取消注册三部分,如果不取消注册会有内存泄漏问题; 事件订阅方可以是一个Activity/Fragment/View等,例如:

public class HelloActivity extends Activity {
	@Override
	public void onStart() {
		super.onStart();
		// 注册订阅方
		EventBus.getDefault().register(this);
	}
	
	// 声明事件处理方法,threadMode表示在哪个线程去调用Subscribers
	@Subscribe(threadMode = ThreadMode.MAIN)  
	public void welcomeUser(User user) {
		CenterToast.makeText(this, "hello,"+ user.name, Toast.LENGTH_SHORT).show();
	}
	
	@Override
	public void onStop() {
		 super.onStop();
		// 取消注册订阅方
		EventBus.getDefault().unregister(this);
	}
}

同样地,事件订阅方也可以是自定义类:

public class UserRequestManager {
	public UserRequestManager {
		// 注册订阅方
		EventBus.getDefault().register(this);
	}

	// 声明事件处理方法,threadMode表示在哪个线程去调用Subscribers
	@Subscribe(threadMode = ThreadMode.MAIN)
	public void addUser(User user) {
		UserRequest reqeust = new UserReqeust(user);
		request.request();
	}
	
	public void release() {
		// 取消注册订阅方
		EventBus.getDefault().unregister(this);
	}
}

2.4 Event Publisher

事件发送方;

public class MainActivity extends Activity {
	
	public void onConfirmClick() {
		User user = new User();
		user.name = nameTextView.getText();
		user.age = Integer.parseInt(ageTextView.getText());
		// 发送Event
		EventBus.getDefault().post(user);
	}
}

三、ThreadMode

官方文档:greenrobot.org/eventbus/do…

在声明事件处理方法的注解中,可以设置ThreadMode,表示当有该Event发送给订阅方时,在哪个线程去调用Subscribers,有五种ThreadMode可供选择:

3.1 ThreadMode: POSTING

默认设置,Subscribers在与事件发送者相同的线程被调用。事件发布是同步进行的,即事件发布后,所有事件订阅者Subscribers都会被依次调用;因为没有线程切换,这种执行方式开销最小;对于一些短时任务且不需要主线程可考虑这种方式;

3.2 ThreadMode: MAIN

Subscribers在主线程被调用。常用来在数据发生变化时更新UI,使用时要避免耗时任务阻塞主线程;

3.3 ThreadMode: MAIN_ORDERED

Subscribers在主线程被调用。MAIN_ORDERED与MAIN不同的地方是, MAIN_ORDERED会先进入队列并稍后发送给Subscribers,例如如果在某个Event中又发送了另外一个Event:

public class Demo1 {
	@Subscribe
	public void addUser(User user) {
		Log.d("DemoTag", "demo1 post start");
		EventBus.getDefault().post(user);
		Log.d("DemoTag", "demo1 post end");
	}
}

public class Demo2 {
	@Subscribe(threadMode = ThreadMode.MAIN)
	public void addUser(User user) {
		Log.d("DemoTag", "demo2:" + user.name);
	}
}

public class Demo3 {
	@Subscribe(threadMode = ThreadMode.MAIN_ORDER)
	public void addUser(User user) {
		Log.d("DemoTag", "demo3:" + user.name);
	}
}

上述例子中,输出结果是:

>demo1 post start
>demo2:jack
>demo1 post end
>demo3:jack

因为MAIN是同步执行,所以MAIN会在方法结束前执行,而MAIN_ORDERED会在方法执行完后再执行;

3.4 ThreadMode: BACKGROUND

Subscribers将在后台线程中被调用。 (1)如果POST线程不是主线程,则Subscribers将直接在后POST线程中被调用; (2)如果发布线程是主线程,则EventBus将使用单个后台线程顺序传递所有事件,所以使用此模式的事件处理方法应快速返回以避免阻塞后台线程。

3.5 ThreadMode: ASYNC

Subscribers始终在单独的线程被调用,即不是POST线程也不是主线程。用于处理一些比较耗时的操作,EventBus内部使用线程池实现该模式的线程调度;

四、Sticky Events

对于一些需要保留较长时间的event,可以使用粘性事件。EventBus最后一个粘性事件保留在内存中,然后在一个新的subscriber初始化完成后就可以获取最新的event,或者可以显式查询最新的event事件。使用方法如下:
(1)sticky event publisher

EventBus.getDefault().postSticky(new User("jack", 18));

(2)sticky event subscriber

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

// 当Activity新建后,就会收到最新的event数据
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {   
    textField.setText(event.message);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);    
    super.onStop();
}

(3)显式查询event

public class Demo{
	public void fun() {
		User user = EventBus.getDefault().getStickyEvent(User.class);
		if(user != null) {
    	// "Consume" the sticky event
    	EventBus.getDefault().removeStickyEvent(user);
    	// Now do something with it
	}
}

五、源码分析

源码分析从两个过程分析:

# 1.事件订阅过程
EventBus.getDefault().register(this);

# 2.事件发布过程
EventBus.getDefault().post("hello");

5.1 EventBus.getDefault()

EventBus.getDefault()是一个单例模式:

public class EventBus {

	static volatile EventBus defaultInstance;

    private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
    
    // 订阅方法查找工具类:通过反射得到类名及方法名及注解,将对象和方法保存到hashmap中
    private final SubscriberMethodFinder subscriberMethodFinder;
    // 
    private final ExecutorService executorService;

	// 单例模式
	public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }
}

5.2 订阅过程

订阅过程如下:

EventBus.getDefault().register(this);

接下来分析register()源码:

public class EventBus {
	
	// key是EventType,value是所有订阅该类型Event的Subscriber列表
	// EventType即事件类型,如本文举例的User或String;
    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
    // key是Subscriber对象,value是该对象所订阅的所有事件类型
	// 与subscriptionsByEventType相对应
    private final Map<Object, List<Class<?>>> typesBySubscriber;
    
	public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        // subscriberMethodFinder通过反射得到类中所有被@Subscribe标记的方法
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
            	// 遍历把Subscribe方法根据EventType添加到对应的hashMap中
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

	private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        // ...
        int size = subscriptions.size();
        for (int i = 0; i <= size; i++) {
        	 // 将对象按优先级添加到EventType所对应的订阅者list中
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }

		// 将事件类型添加到Subscriber订阅的EventType list中
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        subscribedEvents.add(eventType);
        // ...
    }

	// 取消注册过程原理类似,不再分析
	public synchronized void unregister(Object subscriber) {
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType);
            }
            typesBySubscriber.remove(subscriber);
        } else {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }
}

5.3 发布过程

发布过程如下:

EventBus.getDefault().post("hello");

接下来分析post()源码:

public class EventBus {
	// TheadMode所对应的Poster;
	// ThreadMode有5种,poster只有三种是因POSTING、MAIN是同步执行,不需要poster分发
    private final Poster mainThreadPoster;
    private final BackgroundPoster backgroundPoster;
    private final AsyncPoster asyncPoster;
	
	private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
    };
    
	/** Posts the given event to the event bus. */
    public void post(Object event) {
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        if (!postingState.isPosting) {
            postingState.isMainThread = isMainThread();
            postingState.isPosting = true;
            try {
                while (!eventQueue.isEmpty()) {
                	// post单个Event
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

	private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        // ...
        // 根据EventType
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        if (!subscriptionFound) {
            // 没有找到subscriber...
        }
    }

	private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        // 根据EventType得到订阅者list
        synchronized (this) {
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
        	// 遍历list,将Event发送给订阅者
            for (Subscription subscription : subscriptions) {
                postingState.event = event;
                postingState.subscription = subscription;
                boolean aborted = false;
                try {
                	// 实际post方法
                    postToSubscription(subscription, event, postingState.isMainThread);
                    aborted = postingState.canceled;
                } finally {
                    postingState.event = null;
                    postingState.subscription = null;
                    postingState.canceled = false;
                }
                if (aborted) {
                    break;
                }
            }
            return true;
        }
        return false;
    }

	private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED:
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(subscription, event);
                }
                break;
            case BACKGROUND:
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC:
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }
	
	void invokeSubscriber(Subscription subscription, Object event) {
		// 最终通过反射调起Subscriber的事件处理方法
        try {
        subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
        } catch (InvocationTargetException e) {
            handleSubscriberException(subscription, event, e.getCause());
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("Unexpected exception", e);
        }
    }
}

对于postToSubscription中ThreadMode: ASYNC模式,源码如下:

class AsyncPoster implements Runnable, Poster {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    AsyncPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        queue.enqueue(pendingPost);
        // 交给EventBusBuilder.DEFAULT_EXECUTOR_SERVICE的线程池去调度执行
        eventBus.getExecutorService().execute(this);
    }

    @Override
    public void run() {
        PendingPost pendingPost = queue.poll();
        if(pendingPost == null) {
            throw new IllegalStateException("No pending post available");
        }
        // 最终通过反射调起subscriber的事件处理方法
        eventBus.invokeSubscriber(pendingPost);
    }

}

六、annotation-processor

从源码分析可以看出,注册Subscriber的时候,是通过反射来查找用@subscribe注解标识的方法;除了这种方法外,EventBus还提供了annotation-processor的方式来处理注解,这样就省去了反射查找的过程。使用方法如下:

6.1 gradle配置如下

defaultConfig {
    javaCompileOptions {
        annotationProcessorOptions {
            // 自定义annotation-processor生成的类名
            arguments = [eventBusIndex: 'com.bc.demo.SampleEventSubscriberInfoIndex']
        }
    }
}

dependencies {
    implementation 'org.greenrobot:eventbus:3.1.1'
    annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}

6.2 APT自动生成EventBus的订阅关系

在build.gradle中声明了EventBus的annotationProcessorOptions后,对于如下使用EventBus的注解:

public class MainActivity extends Activity{

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void test(String str) {

    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void test2(User user) {

    }
}

在build工程后,APT会自动生成我们在build.graldle中自定义的SampleEventSubscriberInfoIndex类(位于build/generated/ap_generated_sources目录下):

/** This class is generated by EventBus, do not edit. */
public class SampleEventSubscriberInfoIndex implements SubscriberInfoIndex {
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

    static {
        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();

        putIndex(new SimpleSubscriberInfo(com.bc.example.MainActivity.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("test", String.class, ThreadMode.MAIN),
            new SubscriberMethodInfo("test2", com.bc.example.User.class, ThreadMode.MAIN),
        }));

    }

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

    @Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }
    }
}

6.3 修改默认的EventBus

public class App extends Application{
        @Override
        public void onCreate() {
            super.onCreate();
            // 修改默认的EventBus
            EventBus.builder().addIndex(new SampleEventSubscriberInfoIndex()).installDefaultEventBus();
        }
    }

之后,使用EventBus的流程和之前一样。

The End

欢迎关注我,一起解锁更多技能:BC的主页~~💐💐💐 个人信息汇总.png

EventBus README:github.com/greenrobot/…
官方文档:greenrobot.org/eventbus/do…