美文网首页Android开发
Android 应用内常用通信方式

Android 应用内常用通信方式

作者: 糖葫芦_倩倩 | 来源:发表于2017-04-12 18:09 被阅读112次

    从过去一直到现在,常用到的几种通信方式:

    • Handler;

    • 自定义回调接口;

    • 使用广播;

    • EventBus进行应用内通信;
      EventBus in 3 steps:
      1.从它的注册开始:

      EventBus.getDefault().register(this);
      

    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();
                    }
                }
            }
            return defaultInstance;
        }
    

    获取实例的时候使用的是单例模式。
    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<?> subscriberClass = subscriber.getClass();
            //它会查找在当前类里含有注解Subscriber的public的event方法,通俗的说就是查看这个订阅者订阅了哪些事件,因为可能有多个事件,所以是集合。
            List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
            synchronized (this) {
                //循环遍历集合,将订阅者和这些事件subscribe起来,订阅事件
                for (SubscriberMethod subscriberMethod : subscriberMethods) {
                    //遍历所有的方法,依次完成订阅者对方法的订阅
                    subscribe(subscriber, subscriberMethod);
                }
            }
        }
    

    注释中说明:注册订阅者去接受事件,订阅者在它们一旦不想接受事件的时候必须调用unregister(object)方法。
    订阅者处理事件的方法必须使用注解:Subscribe.
    这个Subscribe同时也允许配置像 ThreadMode 一样还有优先级。

    其中:subscriberMethodFinder 是用来查找订阅者订阅了哪些事件的类。
    SubscriberMethod 对象是 订阅方法的对象,包括优先级啊,是否是黏性啊等,类部分源码如下:

      public class SubscriberMethod {
      //订阅的方法
      final Method method;
      //线程
      final 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;
    

    例如:我们订阅的一个类的方法如下:

    public class MediaPlayer {
    
        public MediaPlayer(){
            EventBus.getDefault().register(this);
        }
    
        @Subscribe(sticky = true)
        public void OnMessageEvent(String message){
    
            Log.d("xx","message::"+message);
    
    
            EventBus.getDefault().removeStickyEvent(this);
        }
    
        @Subscribe(sticky = true)
        public void OnMessageEvent2(MessageBean1 message){
    
            Log.d("xx","message::"+message.getMessage());
    
    
            EventBus.getDefault().removeStickyEvent(this);
        }
    }
    

    那么我们对应SubscriberMethod的值就为:

    past.png

    然后我们看下这个findSubscriberMethods方法,是如何查找的:

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
            List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
            //如果缓存map里有,直接取出
            if (subscriberMethods != null) {
                return subscriberMethods;
            }
            //是否忽略注解器生成的index类,默认为false
            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;
            }
        }
    

    其中这个 METHOD_CACHE是缓存订阅者的方法的,key是订阅者,value 是订阅者订阅的事件,还是拿我上面那个MediaPlayer类举例,如图所示:

    1.png

    这个无论是findUsingRefection还是findUsingInfo都会调用findUsingReflectionInSingleClass(findState);这个方法:

    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();
                                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");
                }
            }
        }
    

    通过这个代码我们可以看出来,就是通过反射得到所有的方法,然后再通过是否标识注解,最终保存到

     findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));```
    需要注意的是,如果注解方法中传的参数不是一个,就会抛出`must have exactly 1 parameter`异常;还有就是如果被注解的方法不是public,也会抛出`
    is a illegal @Subscribe method: must be public...`的异常。
    
    
    
    接下来,我们重点看下`subscribe(subscriber, subscriberMethod);`的实现:
    
    // Must be called in synchronized block
        private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
            //获取订阅方法的事件类型
            Class<?> eventType = subscriberMethod.eventType;
            //将这个订阅者还有订阅的事件封装到一个Subscription的对象中
            Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
            //根据订阅者得到订阅者订阅方法一些信息,包括级别,线程等,可参考下图
            CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
            if (subscriptions == null) {
                subscriptions = new CopyOnWriteArrayList<>();
                subscriptionsByEventType.put(eventType, subscriptions);
            } else {
                //如果不为null,看是否定义相同的方法
                if (subscriptions.contains(newSubscription)) {
                    throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                            + eventType);
                }
            }
            //而后根据优先级添加到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;
                }
            }
            //这个`typesBySubscriber` 是
            Map<Object, List<Class<?>>> typesBySubscriber,key 是订阅者,value 是订阅的方法。
            值得话还是看下图3更清晰直白一些;根据订阅者获取订阅的方法集合。
            List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
            if (subscribedEvents == null) {
                subscribedEvents = new ArrayList<>();
                typesBySubscriber.put(subscriber, subscribedEvents);
            }
            //如果不为null 将订阅的方法添加到`subscribedEvents`集合中
            subscribedEvents.add(eventType);
            //判断订阅的事件是否是黏性的
            if (subscriberMethod.sticky) {
                //eventInheritance 是否分发事件
                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>).
                    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 {
                    ////最后都调用这个方法`checkPostStickyEventToSubscription`中就是立刻通知订阅者调用的订阅事件的方法。
                    Object stickyEvent = stickyEvents.get(eventType);
                    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                }
            }
        }
    

    上述CopyOnWriteArrayList<Subscription> subscriptions的值

    2.png

    图3:

    3.png
    我们看下这个checkPostStickyEventToSubscription方法:
    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, Looper.getMainLooper() == Looper.myLooper());
            }
        }
    

    然后就直接invoke 订阅者订阅的事件:

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

    Eventbus大概就是这样的,花了一天的时间。

    相关文章

      网友评论

        本文标题:Android 应用内常用通信方式

        本文链接:https://www.haomeiwen.com/subject/fkxuattx.html