【EventBus】Android EventBus使用和解析

一、使用EventBus

在Android开发中,@Subscribe注解通常用于事件总线(Event Bus)库中,例如GreenRobot的EventBus或Square的Otto。事件总线是一种发布-订阅模式的实现,它允许各个组件之间进行松耦合的通信。

使用@Subscribe注解的方法可以订阅某个类型的事件,当该事件发布时,订阅了该事件的方法会被自动调用。下面是一个使用EventBus的示例:

1.添加依赖

build.gradle文件中添加EventBus的依赖:

implementation 'org.greenrobot:eventbus:3.2.0'

2.定义事件类

创建一个事件类,用于传递数据。

public class MessageEvent {
    public final String message;

    public MessageEvent(String message) {
        this.message = message;
    }
}

3.订阅事件

在需要接收事件的类中,使用@Subscribe注解订阅事件。

import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

public class MyActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 注册EventBus
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 注销EventBus
        EventBus.getDefault().unregister(this);
    }

    // 订阅事件
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MessageEvent event) {
        // 处理事件
        Toast.makeText(this, event.message, Toast.LENGTH_SHORT).show();
    }
}

4.发布事件

在需要发布事件的地方,使用EventBus发布事件。

EventBus.getDefault().post(new MessageEvent("Hello EventBus!"));

在上面的示例中:

  • @Subscribe注解的方法onMessageEvent会在MessageEvent事件发布时被调用。
  • threadMode = ThreadMode.MAIN表示该方法将在主线程中执行,其他选项包括BACKGROUNDASYNC等。

这种模式的优点是可以实现组件之间的松耦合通信,不需要显式地传递引用或实现接口,代码更加简洁和易于维护。

二、@SubScribe注解解析

请先了解什么是自定义注解,点击查看

@Subscribe注解的实现主要依赖于事件总线库的底层机制,这里以GreenRobot的EventBus为例,简要介绍其实现原理。

1. 注解定义

首先,@Subscribe注解本身的定义非常简单,它只是一个标记,用于标识订阅事件的方法。以下是一个简化的示例:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;
}

2. 注册订阅者

当你在代码中调用 EventBus.getDefault().register(this) 时,EventBus会扫描注册对象的所有方法,寻找带有@Subscribe注解的方法。

public void register(Object subscriber) {
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

3. 查找订阅方法

subscriberMethodFinder.findSubscriberMethods 方法会通过反射机制,找到所有带有@Subscribe注解的方法,并将这些方法信息封装到SubscriberMethod对象中。

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    // 通过反射获取所有方法
    Method[] methods = subscriberClass.getDeclaredMethods();
    List<SubscriberMethod> subscriberMethods = new ArrayList<>();
    for (Method method : methods) {
        // 检查是否带有@Subscribe注解
        Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
        if (subscribeAnnotation != null) {
            // 获取方法参数类型
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length != 1) {
                throw new EventBusException("Subscriber " + method.getDeclaringClass() + "." + method.getName() +
                        " must have exactly 1 parameter but has " + parameterTypes.length);
            }
            // 封装成SubscriberMethod对象
            ThreadMode threadMode = subscribeAnnotation.threadMode();
            SubscriberMethod subscriberMethod = new SubscriberMethod(method, threadMode, parameterTypes[0]);
            subscriberMethods.add(subscriberMethod);
        }
    }
    return subscriberMethods;
}

4. 保存订阅者信息

在找到所有订阅方法后,EventBus会将这些方法的信息保存起来,以便在事件发布时能够快速找到对应的订阅者。

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class<?> eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        if (subscriptions.contains(newSubscription)) {
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType);
        }
    }
    subscriptions.add(newSubscription);
}

5. 发布事件

当你调用 EventBus.getDefault().post(event) 时,EventBus会根据事件类型找到所有订阅了该事件的方法,并调用这些方法。

public void post(Object event) {
    PostingThreadState postingState = currentPostingThreadState.get();
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);

    if (!postingState.isPosting) {
        postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
        postingState.isPosting = true;
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {
            while (!eventQueue.isEmpty()) {
                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;
    for (Class<?> clazz = eventClass; clazz != null; clazz = clazz.getSuperclass()) {
        subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
    }
    if (!subscriptionFound) {
        if (logNoSubscriberMessages) {
            Log.d(TAG, "No subscribers registered for event " + eventClass);
        }
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                eventClass != SubscriberExceptionEvent.class) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted;
            try {
                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;
}

6. 调用订阅方法

postToSubscription 方法会根据 ThreadMode 确定在主线程还是后台线程调用订阅方法。

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 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) {
    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);
    }
}

总结

通过上述步骤,EventBus实现了从注册订阅者、查找订阅方法、保存订阅信息到发布事件和调用订阅方法的完整过程。@Subscribe注解的作用就是标识哪些方法是订阅方法,而事件总线库则负责处理这些方法的注册和调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值