EventBus 3.0 源码分析

从EventBus的实例入手

首先从获取的EventBus实例入手,大多数情况下,我们都是通过EventBus.getDefault()获取默认的实例:

/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();// 1
            }
        }
    }
    return defaultInstance;
}

这是一个非常经典的双重锁检查的懒汉式单例。从注解1进入到EventBus的构造方法:

/**
 * Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
 * central bus, consider {@link #getDefault()}.
 */
public EventBus() {
    this(DEFAULT_BUILDER);
}

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

EventBus(EventBusBuilder builder) {
    logger = builder.getLogger();
    subscriptionsByEventType = new HashMap<>();
    typesBySubscriber = new HashMap<>();
    stickyEvents = new ConcurrentHashMap<>();
    mainThreadSupport = builder.getMainThreadSupport();
    mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
    backgroundPoster = new BackgroundPoster(this);
    asyncPoster = new AsyncPoster(this);
    indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
    subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
            builder.strictMethodVerification, builder.ignoreGeneratedIndex);// 2
    logSubscriberExceptions = builder.logSubscriberExceptions;
    logNoSubscriberMessages = builder.logNoSubscriberMessages;
    sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
    sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
    throwSubscriberException = builder.throwSubscriberException;
    eventInheritance = builder.eventInheritance;
    executorService = builder.executorService;
}

很明显,是通过建造者设计模式,传递一个DEFAULT_BUILDER对EventBus的属性就行配置。而EventBusBuilder类的构造方法,默认实现为空:

EventBusBuilder() {
    }

这就完成了EventBus实例的创建工作。

订阅者注册的方法register()

直接看源码:

/**
 * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
 * are no longer interested in receiving events.
 * <p/>
 * Subscribers have event handling methods that must be annotated by {@link Subscribe}.
 * The {@link Subscribe} annotation also allows configuration like {@link
 * ThreadMode} and priority.
 */
public void register(Object subscriber) {
	// 获取订阅者的Class对象
    Class<?> subscriberClass = subscriber.getClass();
	// 获取订阅者对象的所有订阅方法集合
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);// 3
    synchronized (this) {
		// 对订阅方法集合的每个元素进行注册
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);// 4
        }
    }
}

注解3处通过订阅者的Class对象,获取一个SubscriberMethod集合。SubscriberMethod对象其实就是对订阅方法属性的包装类:

/** Used internally by EventBus and generated subscriber indexes. */
public class SubscriberMethod {
    final Method method;// 订阅函数的Method对象
    final ThreadMode threadMode;// 订阅函数的ThreadMode类型
    final Class<?> eventType;// 事件类型,通过这个属性,调用相应的订阅函数
    final int priority;// 事件的优先级
    final boolean sticky;// 是否为黏性事件
    /** Used for efficient comparison */
    String methodString;// 用于判断与其他订阅函数是否相等

    public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
        this.method = method;
        this.threadMode = threadMode;
        this.eventType = eventType;
        this.priority = priority;
        this.sticky = sticky;
    }

    @Override
    public boolean equals(Object other) {
        if (other == this) {
            return true;
        } else if (other instanceof SubscriberMethod) {
            checkMethodString();
            SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other;
            otherSubscriberMethod.checkMethodString();
            // Don't use method.equals because of http://code.google.com/p/android/issues/detail?id=7811#c6
            return methodString.equals(otherSubscriberMethod.methodString);
        } else {
            return false;
        }
    }

    private synchronized void checkMethodString() {
        if (methodString == null) {
            // Method.toString has more overhead, just take relevant parts of the method
            StringBuilder builder = new StringBuilder(64);
            builder.append(method.getDeclaringClass().getName());
            builder.append('#').append(method.getName());
            builder.append('(').append(eventType.getName());
            methodString = builder.toString();
        }
    }

    @Override
    public int hashCode() {
        return method.hashCode();
    }
}

继续查看注解3处的findSubscriberMethods():

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);// 在订阅方法的缓存池中查找是否有该Class的订阅方发集合
    if (subscriberMethods != null) {// 如果有,直接返回对应的订阅方法集合
        return subscriberMethods;
    }
	// 如果是第一次注册订阅者,在方法缓存池中,肯定是没有缓存的方法集合的。
    if (ignoreGeneratedIndex) {
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {
        subscriberMethods = findUsingInfo(subscriberClass);// 5
    }
    if (subscriberMethods.isEmpty()) {// 严谨性判断,抛出异常
        throw new EventBusException("Subscriber " + subscriberClass
                + " and its super classes have no public methods with the @Subscribe annotation");
    } else {
		// 方法集合不为空时,以订阅者的Class对象为Key,放入方法缓存池中
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }
}

对于ignoreGeneratedIndex,该变量是存在于SubscriberMethodFinder类中的,而该类的对象的实例初始化是在哪里完成的呢?其实就是在EventBus()的构造方法里(注解2处),SubscriberMethodFinder()的第三个参数ignoreGeneratedIndex是通过EventBusBuilder对象的ignoreGeneratedIndex传入的,由上面分析可知,EventBus实例化时,EventBusBuilder类的构造方法,默认实现为空,所以ignoreGeneratedIndex取其默认值false:

public class EventBusBuilder {
    ......
    boolean ignoreGeneratedIndex;
    ......
    EventBusBuilder() {
    }

    ......

}

所以findSubscriberMethods()的流程就进入到了注解5

/**
  *FindState类的源码:
  */
static class FindState {
    final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
    final Map<Class, Object> anyMethodByEventType = new HashMap<>();
    final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
    final StringBuilder methodKeyBuilder = new StringBuilder(128);

    Class<?> subscriberClass;
    Class<?> clazz;
    boolean skipSuperClasses;
    SubscriberInfo subscriberInfo;

    void initForSubscriber(Class<?> subscriberClass) {
        this.subscriberClass = clazz = subscriberClass;
        skipSuperClasses = false;
        subscriberInfo = null;
    }

    ......

    void moveToSuperclass() {
        if (skipSuperClasses) {
            clazz = null;
        } else {
            clazz = clazz.getSuperclass();
            String clazzName = clazz.getName();
            /** Skip system classes, this just degrades performance. */
            if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
                clazz = null;
            }
        }
    }
}

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
	// 遍历FIND_STATE_POOL数组的元素,如果有元素不为null,则返回一个该元素;否则直接创建一个FindState
    FindState findState = prepareFindState();
	// 根据传入的订阅者的Class对象,对FindState进行初始化
    findState.initForSubscriber(subscriberClass);
	// 上面的初始化操作,已经对clazz属性进行了赋值(clazz = subscriberClass),所以不为空
    while (findState.clazz != null) {
		// 获取订阅者信息
        findState.subscriberInfo = getSubscriberInfo(findState);// 6
        if (findState.subscriberInfo != null) {
            SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
            for (SubscriberMethod subscriberMethod : array) {
                if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                    findState.subscriberMethods.add(subscriberMethod);
                }
            }
        } else {
            findUsingReflectionInSingleClass(findState);// 7
        }
		// clazz属性指向clazz的父类
        findState.moveToSuperclass();
    }
    return getMethodsAndRelease(findState);// 8
}

注解6

private SubscriberInfo getSubscriberInfo(FindState findState) {
	// 上述对findState初始化操作时,subscriberInfo已被赋值为null,所以不走该代码块
    if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
        SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
        if (findState.clazz == superclassInfo.getSubscriberClass()) {
            return superclassInfo;
        }
    }
	// subscriberInfoIndexes是在EventBus的构造方法中,传入builder,调用SubscriberMethodFinder构造方法为其赋值的,默认为null,所以也不执行
    if (subscriberInfoIndexes != null) {
        for (SubscriberInfoIndex index : subscriberInfoIndexes) {
            SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
            if (info != null) {
                return info;
            }
        }
    }
    return null;
}

综上,因为返回null,所以直接进入注解7的代码:

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        // This is faster than getMethods, especially when subscribers are fat classes like Activities
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
        methods = findState.clazz.getMethods();
        findState.skipSuperClasses = true;
    }
    for (Method method : methods) {
        int modifiers = method.getModifiers();
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length == 1) {
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                if (subscribeAnnotation != null) {
                    Class<?> eventType = parameterTypes[0];
                    if (findState.checkAdd(method, eventType)) {
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
						// 通过上面的各种反射,把订阅者类的订阅函数添加进List
                        findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                    }
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException("@Subscribe method " + methodName +
                        "must have exactly 1 parameter but has " + parameterTypes.length);
            }
        } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
            String methodName = method.getDeclaringClass().getName() + "." + method.getName();
            throw new EventBusException(methodName +
                    " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
        }
    }
}

上面这么多代码,用一句概括就是:通过上面的各种反射,把订阅者类的所有订阅函数添加进List

接着调用注解8处的代码:

private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
    List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
    findState.recycle();
    synchronized (FIND_STATE_POOL) {
		// 将findState放入FIND_STATE_POOL池中
        for (int i = 0; i < POOL_SIZE; i++) {
            if (FIND_STATE_POOL[i] == null) {
                FIND_STATE_POOL[i] = findState;
                break;
            }
        }
    }
    return subscriberMethods;
}

订阅者注册方法的findSubscriberMethods()已分析完毕了。根据register()的执行流程,下面分析对订阅方法集合的每个元素进行注册的流程,也就是注解4

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
	// 订阅方法参数的事件类型
    Class<?> eventType = subscriberMethod.eventType;
	// 构建一个订阅对象
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
	// 将subscriptions以订阅方法参数的事件类型为key,添加到subscriptionsByEventType集合中
    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);
        }
    }
	
	// 根据优先级将newSubscription订阅对象插入到订阅对象集合subscriptions中
    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;
        }
    }
	
	// 将事件类型添加到集合subscribedEvents,再以订阅者对象为Key,subscribedEvents为value,存入typesBySubscriber
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);
	
	// 黏性事件的处理
    if (subscriberMethod.sticky) {
        if (eventInheritance) {
            // Existing sticky events of all subclasses of eventType have to be considered.
            // Note: Iterating over all events may be inefficient with lots of sticky events,
            // thus data structure should be changed to allow a more efficient lookup
            // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
			// stickyEvent在发送事件的时候,将把event存入Map中
            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);// 9
                }
            }
        } else {
            Object stickyEvent = stickyEvents.get(eventType);
            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
        }
    }
}

	/**
     * Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky
     * event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}.
     */
    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);
    }
	
	/**
	  * 传入订阅者对象与订阅方法,构建一个包装类 Subscription(订阅对象)
	  */
	final class Subscription {
    final Object subscriber;
    final SubscriberMethod subscriberMethod;
    /**
     * Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked by queued event delivery
     * {@link EventBus#invokeSubscriber(PendingPost)} to prevent race conditions.
     */
    volatile boolean active;

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

    @Override
    public boolean equals(Object other) {
        if (other instanceof Subscription) {
            Subscription otherSubscription = (Subscription) other;
            return subscriber == otherSubscription.subscriber
                    && subscriberMethod.equals(otherSubscription.subscriberMethod);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return subscriber.hashCode() + subscriberMethod.methodString.hashCode();
    }
}

ok,代码中的注释讲的也比较清楚了,我们来看看处理黏性事件的checkPostStickyEventToSubscription(),也就是注解9:

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
    if (stickyEvent != null) {
        // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
        // --> Strange corner case, which we don't take care of here.
        postToSubscription(newSubscription, stickyEvent, isMainThread());
    }
}

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
	// 根据订阅函数的ThreadMode分别进行处理
    switch (subscription.subscriberMethod.threadMode) {
		// 订阅与发布在同一个线程
        case POSTING:
			// 直接调用订阅函数
            invokeSubscriber(subscription, event);
            break;
		// 订阅在主线程执行
        case MAIN:
			// 判断发布者是否在主线程
            if (isMainThread) {
				// 如果是直接执行
                invokeSubscriber(subscription, event);
            } else {
				// 通过mainThreadPoster(其实就是一个Handler)将订阅事件添加到主线程发布
                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(实则为Runnable)将订阅事件在子线程发布
                backgroundPoster.enqueue(subscription, event);
            } else {
				// 如果不是直接执行
                invokeSubscriber(subscription, event);
            }
            break;
        case ASYNC:
			// 不管发布者在哪个线程发布事件,都通过asyncPoster(实则为Runnable)开启新线程调用订阅函数
            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);
    }
}                                                                                                                      

发布者发送的方法post()

开门见山:

/** Posts the given event to the event bus. */
public void post(Object event) {
	// PostingThreadState保存着事件队列和线程状态信息
    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 {
			// 只要事件队列不为空,那么依次取出头部的元素(事件),交由postSingleEvent()处理,并将其出列
            while (!eventQueue.isEmpty()) {
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}

/** For ThreadLocal, much faster to set (and get multiple values). */
final static class PostingThreadState {
    final List<Object> eventQueue = new ArrayList<>();
    boolean isPosting;
    boolean isMainThread;
    Subscription subscription;
    Object event;
    boolean canceled;
}

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
	// eventInheritance在EventBus的构造方法中通过builder被赋值,在builder的默认值为true
    if (eventInheritance) {
		// 找到事件自身以及父类事件、接口,存于eventTypes集合
        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
        int countTypes = eventTypes.size();
        for (int h = 0; h < countTypes; h++) {
            Class<?> clazz = eventTypes.get(h);
			// 只要该事件的订阅对象集合不为空,postSingleEventForEventType都返回true
            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));
        }
    }
}

/** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */
// // 找到事件自身以及父类事件、接口
private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
    synchronized (eventTypesCache) {
        List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
        if (eventTypes == null) {
            eventTypes = new ArrayList<>();
            Class<?> clazz = eventClass;
            while (clazz != null) {
                eventTypes.add(clazz);
                addInterfaces(eventTypes, clazz.getInterfaces());
                clazz = clazz.getSuperclass();
            }
            eventTypesCache.put(eventClass, eventTypes);
        }
        return eventTypes;
    }
}


private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
		// 取出对应订阅对象的集合
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
		// 遍历subscriptions订阅对象集合,调用postToSubscription()(已在上面分析过)发送事件给调用者
        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;
}

最后一步,订阅者注销unregister()

/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
	// 通过typesBySubscriber找到对应订阅者的事件类型集合
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
	// 事件类型集合不为空
    if (subscribedTypes != null) {
		// 遍历整个typesBySubscriber,调用unsubscribeByEventType()注销掉该订阅者上的事件类型
        for (Class<?> eventType : subscribedTypes) {
            unsubscribeByEventType(subscriber, eventType);
        }
		// 从typesBySubscriber移出该订阅者以及事件类型集合
        typesBySubscriber.remove(subscriber);
    } else {
        logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}

/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
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--;
            }
        }
    }
}

总结

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值