1.Subscribe注解,这里没什么好解释的,添加一个Subscribe注解,在接受Event的方法上使用。
/**
* @Target 该注解的使用范围,当前为方法注解
* @Retention 该注解作用在运行时状态
* 注解内容为threadMode
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe {
//默认的threadMode为POSTING
ThreadMode threadMode() default ThreadMode.POSTING;
}
2.MEventBus类,该类作为组件间作为消息事件传递的中间方,具体使用方法见注释。
public class MEventBus {
private static final String TAG = "MEventBus";
/**
* 该类为单例,因为要保证全局只有一个MEventBus实例
*/
private static MEventBus INSTANCE;
/**
* 调用register的对象的所属类中所有订阅方法的缓存
*/
private final Map<Object, List<MethodManger>> CACHES;
/**
* 线程池
*/
private final ExecutorService executorService;
MEventBus(){
CACHES = new HashMap<>();
executorService = Executors.newCachedThreadPool();
}
/**
* 获取单例,没啥好解释的
*/
public static MEventBus getDefault(){
if (INSTANCE == null){
synchronized (MEventBus.class){
if (INSTANCE == null){
INSTANCE = new MEventBus();
}
}
}
return INSTANCE;
}
/**
* 注册
*/
public void register(Object object){
//空指针判断
AssertNotNullCheck(object, "object is null");
//1.首先获取缓存中的订阅方法集合
List<MethodManger> methodMangers = CACHES.get(object);
//如果步骤1中返回的集合为空,则调用findMethod方法去解析object对象所属的类中所有的订阅方法
//解析结束后,将结果缓存
if (methodMangers == null || methodMangers.size() == 0){
methodMangers = findMethod(object);
CACHES.put(object, methodMangers);
}
}
/**
* 遍历Method,封装,存入CACHES
*/
private List<MethodManger> findMethod(Object object) {
List<MethodManger> list = new ArrayList<>();
//1.获取object对象所属的类中所有的方法(不包括父类)
Method[] methods = object.getClass().getDeclaredMethods();
//2.对所有方法集合进行遍历,获取订阅方法
for (Method method : methods){
//因为只有添加@Subscribe注解的方法才是订阅方法
//3.所以获取方法的Subscribe注解
Subscribe subscribe = method.getAnnotation(Subscribe.class);
//4.若未添加@Subscribe注解,则循环下一个
if (subscribe == null){
continue;
}
//5.如果获取到了订阅方法,则获取注解的threadMode信息
ThreadMode threadMode = subscribe.threadMode();
//6.获取订阅方法的参数类型
Class<?>[] type = method.getParameterTypes();
//7.如果方法参数为空或者参数大于1个,则不满足订阅方法的要求,开始循环下一个
if (type == null || (type != null && type.length > 1)){
continue;
}
//8.解析好的方法信息(method),@Subscribe注解的线程模式(threadMode)和参数类型(type)封装成一个MethodManager对象,该对象的数据结构后面为大家奉上
MethodManger methodManger = new MethodManger(method, threadMode, type[0]);
//9.将获取到的订阅方法信息加入list
list.add(methodManger);
}
//10.返回list
return list;
}
/**
* 反注册订阅类
*/
public void unregister(Object object){
//1.如果该对象所属的类的订阅方法缓存不为空,则remove清空
if (CACHES.get(object) != null){
CACHES.remove(object);
}
}
/**
* post事件
*/
public void post(final Object postEvent){
Log.i(TAG, "post run current thread name is " + Thread.currentThread().getName());
//1.遍历CACHES缓存
Iterator<Object> iterable = CACHES.keySet().iterator();
while (iterable.hasNext()){
//2.如果缓存不为空,则获取key,这里的key即为调用register方法中的入参object
final Object keyObj = iterable.next();
//3.获取2中所述的object对应的订阅方法集合
List<MethodManger> methodMangerList = CACHES.get(keyObj);
//4.遍历该集合
for (MethodManger methodManger : methodMangerList){
//5.判断订阅方法的入参类型是否和post方法的入参类型一致,如果一致,则进入判断
if (methodManger.getType() == postEvent.getClass()){
//6.获取满足条件5的订阅方法,取出对应的信息
final Method method = methodManger.getMethod();
//7.获取threadMode,判断线程模式,当前的线程是调用post方法时所在的线程
switch (methodManger.getThreadMode()){
//8-1 如果是POSTING,则直接在当前线程执行消息事件接收
case POSTING:
invoke(method, keyObj, postEvent);
break;
case MAIN:
//8-2 如果是MAIN,则判断当前线程是否为主线程,如果否,则通过主线程Looper实例化Handler,post到主线程执行消息事件接受
//如果当前线程为主线程,则直接执行消息事件接收
if (Looper.myLooper() == Looper.getMainLooper()){
invoke(method, keyObj, postEvent);
}else{
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
invoke(method, keyObj, postEvent);
}
});
}
break;
//8-3 如果是BACKGROUND,则判断当前线程是否为主线程,如果是,则开一个新的子线程去执行消息事件接收
// 如果当前线程为子线程,则直接执行消息事件接收
case BACKGROUND:
if (Looper.myLooper() == Looper.getMainLooper()){
executorService.execute(new Runnable() {
@Override
public void run() {
invoke(method, keyObj, postEvent);
}
});
}else {
invoke(method, keyObj, postEvent);
}
break;
//8-4 如果是ASYNC,则不管当前线程是什么,直接开一个新的子线程去执行消息事件接收
case ASYNC:
executorService.execute(new Runnable() {
@Override
public void run() {
invoke(method, keyObj, postEvent);
}
});
break;
default:
break;
}
}
}
}
}
/**
* 通过反射调用订阅者方法
*/
private void invoke(Method method, Object keyObj, Object postEvent) {
try {
method.invoke(keyObj, postEvent);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
/**
* 空指针检查
*/
private void AssertNotNullCheck(Object object, String object_is_null) {
if (object == null){
throw new NullPointerException(object_is_null);
}
}
}
3.MethodManager封装类,对订阅方法的注解信息,入参类型等信息进行封装。
public class MethodManger {
/**
* 通过反射获取的method方法对象
*/
private Method method;
/**
* @Subscribe注解的threadMode的值
*/
private ThreadMode threadMode;
/**
* 订阅方法的入参类型
*/
private Class<?> type;
public MethodManger(Method method, ThreadMode threadMode, Class<?> type) {
this.method = method;
this.threadMode = threadMode;
this.type = type;
}
public Method getMethod() {
return method;
}
public ThreadMode getThreadMode() {
return threadMode;
}
public Class<?> getType() {
return type;
}
}
4. ThreadMode 线程模式,用于@Subscribe注解,直接从源码中复制。
public enum ThreadMode {
/**
* Subscriber will be called directly in the same thread, which is posting the event. This is the default. Event delivery
* implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for
* simple tasks that are known to complete in a very short time without requiring the main thread. Event handlers
* using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.
*/
POSTING,
/**
* On Android, subscriber will be called in Android's main thread (UI thread). If the posting thread is
* the main thread, subscriber methods will be called directly, blocking the posting thread. Otherwise the event
* is queued for delivery (non-blocking). Subscribers using this mode must return quickly to avoid blocking the main thread.
* If not on Android, behaves the same as {@link #POSTING}.
*/
MAIN,
/**
* On Android, subscriber will be called in Android's main thread (UI thread). Different from {@link #MAIN},
* the event will always be queued for delivery. This ensures that the post call is non-blocking.
*/
MAIN_ORDERED,
/**
* On Android, subscriber will be called in a background thread. If posting thread is not the main thread, subscriber methods
* will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single
* background thread, that will deliver all its events sequentially. Subscribers using this mode should try to
* return quickly to avoid blocking the background thread. If not on Android, always uses a background thread.
*/
BACKGROUND,
/**
* Subscriber will be called in a separate thread. This is always independent from the posting thread and the
* main thread. Posting events never wait for subscriber methods using this mode. Subscriber methods should
* use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number
* of long running asynchronous subscriber methods at the same time to limit the number of concurrent threads. EventBus
* uses a thread pool to efficiently reuse threads from completed asynchronous subscriber notifications.
*/
ASYNC
}
5.在MainActivity中进行接收
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnStart = findViewById(R.id.btn_start_second);
btnStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SecondAcvitity.class);
startActivity(intent);
}
});
MEventBus.getDefault().register(this);
}
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void showLog(String value){
Log.i(TAG, "showLog get value is " + value + " and current thread name is " + Thread.currentThread().getName());
}
@Override
protected void onDestroy() {
super.onDestroy();
MEventBus.getDefault().unregister(this);
}
6.在SecondActivity进行发送消息。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
btnPost = findViewById(R.id.btn_post);
btnPost.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// new Thread(new Runnable() {
// @Override
// public void run() {
MEventBus.getDefault().post("你好呀,MainActivity,我是SecondActivity");
// }
// }).start();
}
});
}
至此消息的发送和接收已经基本完成了。此文仅探讨EventBus的核心机制,对于一些细节,如strict等没有进行实现,有兴趣的童鞋可以去查看EventBus源码。
参考:网易云课堂,android高级开发工程师公开课