目录
3.1.1 threadMode: 配置消息接收是在哪个线程
3.2.2 collectSubscribers收集订阅类和订阅的方法信息
3.3.2 查找订阅方法findSubscriberMethods()
1. 架构图
从官网的架构图可以看出来,EventBus可以将发布者和订阅者解偶,这就是EventBus最大的方便之处。
2. 使用
请参考我的另外一篇文章:https://blog.csdn.net/fwt336/article/details/103133850
3. 源码解析
3.1 @Subscribe注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
/**
* If true, delivers the most recent sticky event (posted with
* {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
*/
boolean sticky() default false;
/** Subscriber priority to influence the order of event delivery.
* Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
* others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
* delivery among subscribers with different {@link ThreadMode}s! */
int priority() default 0;
}
从Subscribe注解中的元注解可以看出来,我们的Subscribe是运行时注解,只能在运行时进行解析,也只能注册Method方法。
里面有三个参数可以进行配置:threadMode,sticky, priority。
3.1.1 threadMode: 配置消息接收是在哪个线程
有5种线程设置方式:
POSTING : 消息的接收与消息发送在同一个线程处理
MAIN : 消息在UI线程处理,但是如果发送线程是在UI线程,消息将会直接被调用处理,将阻塞调用线程。
MAIN_ORDERED : 消息在主线程处理,但是跟MAIN不一样的是,消息将会加入到队列中进行处理,不回堵塞调用线程。
BACKGROUND : 消息在后台线程处理,如果发送线程不是UI线程,则将会直接在发送线程处理消息。如果发送线程是UI线程,那么将使用一个后台线程,按照事件顺序处理。
ASYNC :消息将在单独的线程中调用,如果消息处理很耗时,可以采用这种方法,最终是EventBus使用线程池来处理消息。
3.1.2 sticky : 粘性事件
默认为false,如果设置为true,其作用与粘性广播的使用类似。也就是我们的粘性事件可以在事件注册前发送,当注册事件,并设置接收粘性事件后,将会接收到粘性事件。如果在注册之前发送了两个粘性事件,则只会接收到最后一个事件。
调用时,需用使用postSticky方法:
EventBus.getDefault().postSticky(new EventObject());
接收方法添加sticky = true即可,例如:
@Subscribe(threadMode = ThreadMode.Main, sticky = true)
stickyEventReceiver() {}
3.1.3 priority : 接收事件的优先级
在注册时将会用到这个参数,根据优先级加入到订阅者列表中。
3.2 注解解析
3.2.1 注解类上的元注解
先看看注解处理器上的注解修饰
@SupportedAnnotationTypes("org.greenrobot.eventbus.Subscribe")
@SupportedOptions(value = {"eventBusIndex", "verbose"})
public class EventBusAnnotationProcessor extends AbstractProcessor {}
SupportedAnnotationTypes : 指定处理器支持的处理类型
SupportedOptions : 用来表示所支持的附加选项
在运行apt命令行工具的时候,可以通过-A来传递额外的参数给注解处理器,如-Averbose=true。当通过 supportedOptions方法声明了所能识别的附加选项之后,注解处理器就可以在运行时刻通过AnnotationProcessorEnvironment的getOptions方法获取到选项的实际值。
public static final String OPTION_EVENT_BUS_INDEX = "eventBusIndex";
public static final String OPTION_VERBOSE = "verbose";
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
Messager messager = processingEnv.getMessager();
String index = processingEnv.getOptions().get(OPTION_EVENT_BUS_INDEX);
if (index == null) {
messager.printMessage(Diagnostic.Kind.ERROR, "No option " + OPTION_EVENT_BUS_INDEX +
" passed to annotation processor");
return false;
}
verbose = Boolean.parseBoolean(processingEnv.getOptions().get(OPTION_VERBOSE));
int lastPeriod = index.lastIndexOf('.');
String indexPackage = lastPeriod != -1 ? index.substring(0, lastPeriod) : null;
round++;
if (verbose) {
messager.printMessage(Diagnostic.Kind.NOTE, "Processing round " + round + ", new annotations: " +
!annotations.isEmpty() + ", processingOver: " + env.processingOver());
}
return true;
}
eventBusIndex选项值是注解器生成的一个类的路径,是必须要传递的一个值,它是在build.gradle中进行传递的:
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [eventBusIndex: 'org.greenrobot.eventbusperf.MyEventBusIndex']
}
}
}
}
后面我们将会使用到这个类。
而verbose则是一个控制调试信息打印的变量,可传可不传。
3.2.2 collectSubscribers收集订阅类和订阅的方法信息
而我们的主要工作在下面三个方法上:
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
Messager messager = processingEnv.getMessager();
try {
// ...
collectSubscribers(annotations, env, messager); // 1.收集注解类和方法
checkForSubscribersToSkip(messager, indexPackage); // 2.过滤事件
if (!methodsByClass.isEmpty()) {
createInfoIndexFile(index); // 3.生成MyEventBusIndex类
} else {
messager.printMessage(Diagnostic.Kind.WARNING, "No @Subscribe annotations found");
}
// ...
} catch (RuntimeException e) {
}
return true;
}
看下collectSubscribers源码:
private void collectSubscribers(Set<? extends TypeElement> annotations, RoundEnvironment env, Messager messager) {
for (TypeElement annotation : annotations) {
Set<? extends Element> elements = env.getElementsAnnotatedWith(annotation);
for (Element element : elements) {
if (element instanceof ExecutableElement) {
ExecutableElement method = (ExecutableElement) element;
if (checkHasNoErrors(method, messager)) {
TypeElement classElement = (TypeElement) method.getEnclosingElement();
methodsByClass.putElement(classElement, method);
}
} else {
messager.printMessage(Diagnostic.Kind.ERROR, "@Subscribe is only valid for methods", element);
}
}
}
}
它会遍历每一个Subscriber标志信息,将标注的类和标注方法对应起来,存储到methodByClass里,方便后面使用。
同时,它还会检查方法是否被static修饰了,是不是被public修饰的,是否只有一个参数,也就是我们的事件参数只能是一个。
private boolean checkHasNoErrors(ExecutableElement element, Messager messager) {
if (element.getModifiers().contains(Modifier.STATIC)) { // 检查是否有static修饰
messager.printMessage(Diagnostic.Kind.ERROR, "Subscriber method must not be static", element);
return false;
}
if (!element.getModifiers().contains(Modifier.PUBLIC)) { // 检查是不是public修饰的
messager.printMessage(Diagnostic.Kind.ERROR, "Subscriber method must be public", element);
return false;
}
List<? extends VariableElement> parameters = ((ExecutableElement) element).getParameters();
if (parameters.size() != 1) { // 检查参数是否有且只有一个
messager.printMessage(Diagnostic.Kind.ERROR, "Subscriber method must have exactly 1 parameter", element);
return false;
}
return true;
}
而我们收集到的数据methodsByClass将在checkForSubscribersToSkip方法和生成MyEventBusIndex时的writeIndexLines()方法中使用到。
checkForSubscribersToSkip流程:
private void checkForSubscribersToSkip(Messager messager, String myPackage) {
for (TypeElement skipCandidate : methodsByClass.keySet()) {
TypeElement subscriberClass = skipCandidate;
while (subscriberClass != null) {
if (!isVisible(myPackage, subscriberClass)) { // 判断方法是否可见,public修饰或在同一个包内
boolean added = classesToSkip.add(skipCandidate);
break;
}
List<ExecutableElement> methods = methodsByClass.get(subscriberClass);
if (methods != null) {
for (ExecutableElement method : methods) {
String skipReason = null;
VariableElement param = method.getParameters().get(0);
TypeMirror typeMirror = getParamTypeMirror(param, messager);
if (!(typeMirror instanceof DeclaredType) ||
!(((DeclaredType) typeMirror).asElement() instanceof TypeElement)) {
skipReason = "event type cannot be processed";
}
if (skipReason == null) {
TypeElement eventTypeElement = (TypeElement) ((DeclaredType) typeMirror).asElement();
if (!isVisible(myPackage, eventTypeElement)) {
skipReason = "event type is not public";
}
}
if (skipReason != null) {
boolean added = classesToSkip.add(skipCandidate);
break;
}
}
}
// 遍历父类中的方法
subscriberClass = getSuperclass(subscriberClass);
}
}
}
a.首先会遍历所有标了Subscribe注解的类;
b.判断类是否可见,即是否有public修饰,或是否与MyEventBusIndex.java在同一个包内,或没有修饰符。不可见则放入classToSkip需要过滤的容器中;
private boolean isVisible(String myPackage, TypeElement typeElement) {
Set<Modifier> modifiers = typeElement.getModifiers();
boolean visible;
if (modifiers.contains(Modifier.PUBLIC)) {
visible = true;
} else if (modifiers.contains(Modifier.PRIVATE) || modifiers.contains(Modifier.PROTECTED)) {
visible = false;
} else {
String subscriberPackage = getPackageElement(typeElement).getQualifiedName().toString();
if (myPackage == null) {
visible = subscriberPackage.length() == 0;
} else {
visible = myPackage.equals(subscriberPackage);
}
}
return visible;
}
c.遍历Subscribe标注的类对应的所有注解方法;
d.判断Class是否是类或接口DeclaredType,不是则放入classToSkip需要过滤的容器中;
e.遍历父类,重复b,c,d方法
3.2.3 生成MyEventBusIndex类
主要是看下writeIndexLines方法,其他生成的代码都是固定的:
private void writeIndexLines(BufferedWriter writer, String myPackage) throws IOException {
for (TypeElement subscriberTypeElement : methodsByClass.keySet()) {
if (classesToSkip.contains(subscriberTypeElement)) {
continue;
}
String subscriberClass = getClassString(subscriberTypeElement, myPackage);
if (isVisible(myPackage, subscriberTypeElement)) {
writeLine(writer, 2,
"putIndex(new SimpleSubscriberInfo(" + subscriberClass + ".class,",
"true,", "new SubscriberMethodInfo[] {");
List<ExecutableElement> methods = methodsByClass.get(subscriberTypeElement);
writeCreateSubscriberMethods(writer, methods, "new SubscriberMethodInfo", myPackage);
writer.write(" }));\n\n");
} else {
writer.write(" // Subscriber not visible to index: " + subscriberClass + "\n");
}
}
}
也就是遍历我们收集到的带有@Subscribe注解的类,过滤出有效的类,再将我们的注册类名和注解参数封装起来,放入到SUBSCRIBER_INDEX这个map中,对外提供查找方法getSubscriberInfo()。
看下生成的这个类长什么样子:
package org.greenrobot.eventbusperf;
import org.greenrobot.eventbus.meta.SimpleSubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberMethodInfo;
import org.greenrobot.eventbus.meta.SubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberInfoIndex;
import org.greenrobot.eventbus.ThreadMode;
import java.util.HashMap;
import java.util.Map;
/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(TestRunnerActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onTestEvent", TestEvent.class, ThreadMode.MAIN),
}));
putIndex(new SimpleSubscriberInfo(TestActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onDemoEvent", TestEvent.class, ThreadMode.MAIN_ORDERED, 0, true),
}));
}
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;
}
}
}
3.3 注册流程解析
3.3.1 注册流程
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
大致流程是:通过注册方法所在的类名,查找到所有注册类的订阅方法,然后在遍历每个订阅方法,将事件类型eventType跟订阅器Subscription(内部封装了订阅类所需的所有参数)存入到subscriptionsByEventType中,而subscriptionsByEventType在进行消息分发查找时将会用到Subscription数据。
3.3.2 查找订阅方法findSubscriberMethods()
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
从源码可以发现查找方法名有两种方式,一种时反射,另一种是通过获取注解处理器生成的类MyEventBusIndex获取,而默认ignoreGeneratedIndex是false。
反射查找所有方法则是通过获取注册类的所有方法,找到可见的且标注了@Subscribe注解的方法,然后封装成SubscriberMethod类返回一个SubscriberMethod的list。
通过注解类查找定制类型的所有方法
注解处理器生成类的注入:
/** Adds an index generated by EventBus' annotation preprocessor. */
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if (subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
待用EventBusBuilder的addInex()方法后,这样,我们的注解处理器的作用才真正达到了。
但是,当没有配置注解器时,将强制使用反射方式进行处理。
那么问题来了,你在使用EventBus时配置了EventBusIndex类吗?
3.3.3 存储订阅类信息subscribe()
我们将subscribe()方法分为三个部分来看。
a.存储订阅者信息
将订阅事件和订阅方法封装到Subscription中,然后以列表的形式存储在subscriptionsByEventType中,事件类型为key。而订阅事件发送的时候,也会从subscriptionsByEventType map中,将事件对应的subscriber订阅器信息取出来,然后进行消息事件的反射。
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);
}
}
// ...处理事件优先级
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//...处理粘性事件
}
b. 处理消息事件优先级
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// ...
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
// ...
}
每个方法插入列表的时候,将根据优先级插入,然后将列表保存在subscriptionsByEventType中。
c. 处理粘性消息
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// ...
if (subscriberMethod.sticky) {
if (eventInheritance) {
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
eventInheritance默认为true,所以会遍历整个stickyEvents,然后将stickyEvents中的消息事件分发出去:
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
postToSubscription(newSubscription, stickyEvent, isMainThread()); // 分发给对应的poster处理
}
}
3.3.4 取消订阅
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
取消订阅就比较简单,只是将subscriptionsByEventType中对应的事件的注册类移除即可。
3.4 消息发送和接收解析
3.4.1 postSticky粘性消息
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
通过源码我们可以发现,postSticky最终还是调用了post方法,只是会多一步将event加入stickyEvents中存储起来,当注册的时候会判断是否有对应可能的粘性事件。
3.4.2 post普通消息
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;
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;
}
}
}
在post有会保存当前线程的一个状态,即PostingThreadState。但是查了下没有set方法,只有get,原来当没有有效值时,内部创建了一个默认的PostingThreadState。
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
当事件消息过来后,首先会通过ThreadLocal找到当前的线程PostingThreadState状态,获取到PostingThreadState中的队列,然后加入到队列中,如果此时线程正在分发消息,则退出,最后加入到消息将由正在分发消息的线程分发。
下面看下具体到消息分发:postSingleEvent(eventQueue.remove(0), postingState);
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
接着会获取到事件的类名,通过postSingleEventForEventType方法继续分发事件。
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 = false;
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;
}
最终通过事件类名从subscriptionsByEventType集合中找到对应的所有订阅类,而我们在注册的时候,将事件类名和订阅类存入到类subscriptionsByEventType中。
找到所有的订阅类subscriptions后,遍历所有订阅类,然后通过postToSubscription方法进行具体的消息分发处理。
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);
}
}
最终会通过初始化时的分发器,根据threadMode类型进行消息的处理。如果是posting线程则直接调用invokeSubscriber处理,如果是后台线程或者异步线程,则都是调用EventBus的线程池进行处理消息队列中的消息,而UI线程则是通过handler进行处理。
看下mainPoster的代码就知道了,确实是通过new HandlerPoster处理的,而HandlerPoster继承了Handler,并实现了Poster接口:
public interface MainThreadSupport {
boolean isMainThread();
Poster createPoster(EventBus eventBus);
class AndroidHandlerMainThreadSupport implements MainThreadSupport {
private final Looper looper;
public AndroidHandlerMainThreadSupport(Looper looper) {
this.looper = looper;
}
@Override
public boolean isMainThread() {
return looper == Looper.myLooper();
}
@Override
public Poster createPoster(EventBus eventBus) {
return new HandlerPoster(eventBus, looper, 10);
}
}
}
重点来了,最后所有消息处理都是通过反射实现的:
void invokeSubscriber(PendingPost pendingPost) {
Object event = pendingPost.event;
Subscription subscription = pendingPost.subscription;
PendingPost.releasePendingPost(pendingPost);
if (subscription.active) {
invokeSubscriber(subscription, event);
}
}
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);
}
}
终于看到了久违的invoke方法!
4. 总结
EventBus可以通过注解和反射两种方式来获取到订阅者的相关信息,然后通过UI线程或内部的线程池处理发送的消息事件,最终是通过反射实现订阅者方法的调用,有订阅者处理消息。