EventBus

持续更新。。。。。。。ing


一、简介

  主要功能是替代Intent、Handler、BroadCast在Fragment、Activity、Service、线程之间传递消息。他的最牛逼优点是开销小,代码简洁,解耦代码。当一个Android应用功能越来越多的时候,保证应用的各个部分之间高效的通信将变得越来越困难。

在应用中的多个地方,控件经常需要根据某个状态来更新他们显示的内容。这种场景常见的解决方式就是定义一个接口,需要关注该事件的控件来实现这个接口。然后事件触发的地方来注册/取消注册这些对该事件感兴趣的控件。

githup:https://github.com/greenrobot/EventBus


还有一个类似的项目https://github.com/square/otto


EventBus可以订阅的函数有:

onEvent (Object  object )
onEventMainThread (Object  object )
onEventBackgroundThread (Object  object )
onEventAsync (Object  object )

这四种订阅函数都是使用onEvent开头的,它们的功能稍有不同,在介绍不同之前先介绍两个概念:
告知观察者事件发生时通过EventBus.post函数实现,这个过程叫做事件的发布,观察者被告知事件发生叫做事件的接收,是通过下面的订阅函数实现的。

onEvent:如果使用onEvent作为订阅函数,那么该事件在哪个线程发布出来的,onEvent就会在这个线程中运行,也就是说发布事件和接收事件线程在同一个线程。使用这个方法时,在onEvent方法中不能执行耗时操作,如果执行耗时操作容易导致事件分发延迟。
onEventMainThread:如果使用onEventMainThread作为订阅函数,那么不论事件是在哪个线程中发布出来的,onEventMainThread都会在UI线程中执行,接收事件就会在UI线程中运行,这个在Android中是非常有用的,因为在Android中只能在UI线程中跟新UI,所以在onEvnetMainThread方法中是不能执行耗时操作的。
onEventBackground:如果使用onEventBackgrond作为订阅函数,那么如果事件是在UI线程中发布出来的,那么onEventBackground就会在子线程中运行,如果事件本来就是子线程中发布出来的,那么onEventBackground函数直接在该子线程中执行。
onEventAsync:使用这个函数作为订阅函数,那么无论事件在哪个线程发布,都会创建新的子线程在执行onEventAsync.



二、register方法

public void register(Object subscriber) {  
        register(subscriber, DEFAULT_METHOD_NAME, false, 0);  
    }  
 public void register(Object subscriber, int priority) {  
        register(subscriber, DEFAULT_METHOD_NAME, false, priority);  
    }  
public void registerSticky(Object subscriber) {  
        register(subscriber, DEFAULT_METHOD_NAME, true, 0);  
    }  
public void registerSticky(Object subscriber, int priority) {  
        register(subscriber, DEFAULT_METHOD_NAME, true, priority);  
    }  


本质上就调用了同一个:

在下面的代码中 subscriberMethodFinder.findSubscriberMethods(subscriber.getClass())

的重要作用就是根据订阅者找到该订阅者的订阅函数,然后将封装成SubscriberMethod

简单看看该对象

final class SubscriberMethod {
    final Method method;
    final ThreadMode threadMode;
    /**订阅的方法参数类型*/
    final Class<?> eventType;
    /** Used for efficient comparison */
    String methodString;

      ...............
      ...............

}

好了,回到重点:

    private synchronized void register(Object subscriber, boolean sticky, int priority) {
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod, sticky, priority);
        }
    }
    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    	//获取订阅者的全限定类名
        String key = subscriberClass.getName();
        List<SubscriberMethod> subscriberMethods;
        synchronized (methodCache) {
        	//判断是否有缓存,有缓存直接返回缓存
            subscriberMethods = methodCache.get(key);
        }
        
        //第一次进来subscriberMethods肯定是Null 
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
        
        subscriberMethods = new ArrayList<SubscriberMethod>();
        Class<?> clazz = subscriberClass;
        HashSet<String> eventTypesFound = new HashSet<String>();
        StringBuilder methodKeyBuilder = new StringBuilder();
        while (clazz != null) {
            String name = clazz.getName();
              //过滤掉系统类  
            if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
                // Skip system classes, this just degrades performance
                break;
            }

            // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
            //包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                String methodName = method.getName();
                //找到方法名以onEvent开头的方法
                if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {
                    //获取方法的修饰符,包括public(1), protected(4), private(2), final(16), static(8), abstract(1024) 和interface(512)及Java虚拟机的常数
                    int modifiers = method.getModifiers();
                    //方法是PUBLIC的且方法不能是ABSTRACT | STATIC | BRIDGE | SYNTHETIC中的任何一个。
                    if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                    	
                    	//获取方法参数类型
                        Class<?>[] parameterTypes = method.getParameterTypes();
                        //呵呵。。参数只能有一个
                        if (parameterTypes.length == 1) {
                            String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
                            ThreadMode threadMode;
                            if (modifierString.length() == 0) {
                            	//订阅的方法是onEvent
                                threadMode = ThreadMode.PostThread;
                            } else if (modifierString.equals("MainThread")) {
                            	//订阅的方法是onEventMainThread
                                threadMode = ThreadMode.MainThread;
                            } else if (modifierString.equals("BackgroundThread")) {
                            	//订阅的方法是onEventBackgroundThread
                                threadMode = ThreadMode.BackgroundThread;
                            } else if (modifierString.equals("Async")) {
                            	//订阅的方法是onEventAsync
                                threadMode = ThreadMode.Async;
                            } else {
                                if (skipMethodVerificationForClasses.containsKey(clazz)) {
                                    continue;
                                } else {
                                    throw new EventBusException("Illegal onEvent method, check for typos: " + method);
                                }
                            }
                            
                            Class<?> eventType = parameterTypes[0];
                            methodKeyBuilder.setLength(0);
                            methodKeyBuilder.append(methodName);
                            methodKeyBuilder.append('>').append(eventType.getName());
                            String methodKey = methodKeyBuilder.toString();
                            if (eventTypesFound.add(methodKey)) {
                                // Only add if not already found in a sub class
                            	//封装一个订阅方法对象,这个对象包含Method对象,threadMode对象,eventType对象  
                                subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
                            }
                        }
                    } else if (!skipMethodVerificationForClasses.containsKey(clazz)) {
                        Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."
                                + methodName);
                    }
                }
            }
            //还会遍历父类的订阅函数  
            clazz = clazz.getSuperclass();
        }
         //最后加入缓存,第二次使用直接从缓存拿  
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
                    + ON_EVENT_METHOD_NAME);
        } else {
            synchronized (methodCache) {
                methodCache.put(key, subscriberMethods);
            }
            return subscriberMethods;
        }
    }


找到订阅者订阅的函数列表后,根据每个订阅函数的参数类型(以后就是发布对象)

    // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
    	//从订阅方法中拿到订阅事件的类型 ,就是onEvent一些列方法里的参数类型
    	Class<?> eventType = subscriberMethod.eventType;
    	 //通过订阅事件类型,找到所有的订阅(Subscription),订阅中包含了订阅者,订阅方法  、等级
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        //创建一个新的订阅 
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
        if (subscriptions == null) {
        	 //将新建的订阅加入到这个事件类型对应的所有订阅列表  
        	subscriptions = new CopyOnWriteArrayList<Subscription>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
        	  //如果有订阅列表,检查是否已经加入过  
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

        // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
        // subscriberMethod.method.setAccessible(true);

        //根据优先级插入订阅  
        int size = subscriptions.size();
        for (int i = 0; i <= size; i++) {
            if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }

        //将这个订阅事件加入到订阅者的订阅事件列表中   
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<Class<?>>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

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

先不讨论sticky

看得出该函数主要干了2件事(不讨论sticky),

1.根据发布类型,建立一个Subscription订阅列表,看得出Subscription封装了订阅者、SubscriberMethod和订阅的等级(数字越大,订阅越靠前);

2.根据订阅者,建立一个订阅的事件类型列表。

final class Subscription {
    /**  订阅者*/
    final Object subscriber; 
    /**封装 的订阅方法*/
    final SubscriberMethod subscriberMethod;
    /** 等级*/
    final int priority;
    /**
     * 只要unregister(Object)方法被调用,active就会变成false
     * 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;
    ......................
    ......................
}



到此regist方法分析完。。注册完后就可以发布了。




三、POST方法

public void post(Object event)
public void postSticky(Object event) 


先看post(Ocject event)

 /** For ThreadLocal, much faster to set (and get multiple values). */
    final static class PostingThreadState {
    
    	//这里的Object是什么?就是你要发布时的event(简单的说就是一个对象)
        final List<Object> eventQueue = new ArrayList<Object>();
        boolean isPosting;
        boolean isMainThread;
        Subscription subscription;
        Object event;
        boolean canceled;
    }


    //任何地方都可以调用
    /** Posts the given event to the event bus. */
    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;
        if (eventInheritance) {
        	 //找到eventClass对应的事件,包含父类对应的事件和接口对应的事件  
            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) {
                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 = 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;
    }



 private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    	//第一个参数就是传入的订阅,第二个参数就是对于的分发对象,第三个参数非常关键:是否在主线程  
    	switch (subscription.subscriberMethod.threadMode) {
        	//当前页面直接发布   
    		case PostThread:
            	//直接在本线程中调用订阅函数  
                invokeSubscriber(subscription, event);
                break;
             //要发布到主线程
            case MainThread:
                if (isMainThread) {
                	 //如果直接在主线程,那么直接在本现场中调用订阅函数  
                    invokeSubscriber(subscription, event);
                } else {
                	//如果不在主线程,那么通过handler实现在主线程中执行
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            //要发布到后台线程
            case BackgroundThread:
                if (isMainThread) {
                	 //如果当前是主线程,创建一个runnable丢入线程池中  
                    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);
        }
    }




五、


四个参数

subscriber 是我们扫描类的对象,也就是我们代码中常见的this;

methodName 这个是写死的:“onEvent”,用于确定扫描什么开头的方法,可见我们的类中都是以这个开头。

sticky 这个参数,解释源码的时候解释,暂时不用管

priority 优先级,优先级越高,在调用的时候会越先调用。

 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());




实例:“设置壁纸”



EventBus规定onEvent方法固定作为订阅者接受事件的方法,应该是参考了“约定优于配置”思想。

  1. 定义EventModel,作为组件间通信传递数据的载体

public class WallpaperEvent {

private Drawable wallpaper;

public WallpaperEvent(Drawable wallpaper) {
    this.wallpaper = wallpaper;
}

public Drawable getWallpaper() {
    return wallpaper;
}

public void setWallpaper(Drawable wallpaper) {
    this.wallpaper = wallpaper;
}
}


  1. 定义订阅者,最重要的是onEvent方法

public class BaseActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    EventBus.getDefault().register(this);
    initWallpaper();
}

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

public void onEvent(WallpaperEvent wallpaperEvent) {
    // AppConfig.sWallpaperDrawable as a global static var
    AppConfig.sWallpaperDrawable = wallpaperEvent.getWallpaper();
    initWallpaper();
}

private void initWallpaper() {
    // support custom setting the wallpaper
    // 根据AppConfig.sWallpaperDrawable,默认值等设置当前Activity的背景壁纸
    // ...
}
}


  1. 通过post()方法在任何地方发布消息(壁纸,准确的说是WallpaperEvent)给所有的BaseActivity子类,举个例子:

    private void downloadWallpapper(String src) {
        ImageLoader.getInstance().loadImage(src, new SimpleImageLoadingListener() {
            @Override
            public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {

            BitmapDrawable wallpaper = new BitmapDrawable(loadedImage);
            // presist the image url for cache
            saveWallpaper(imageUri);

            // notify all base activity to update wallpaper
            EventBus.getDefault().post(new WallpaperEvent(wallpaper));


            Toast.makeText(WallpapeEventBusrActivity.this,
                    R.string.download_wallpaper_success,
                    Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
            Toast.makeText(WallpaperActivity.this,
                    R.string.download_wallpaper_fail,
                    Toast.LENGTH_SHORT).show();
        }
    });
}

重点就是这句:
// 在任何地方调用下面的方法,即可动态全局实现壁纸设置功能
EventBus.getDefault().post(new WallpaperEvent(wallpaper));





参考:http://blog.csdn.net/harvic880925/article/details/40787203

            http://blog.csdn.net/yuanzeyao/article/details/38174537

            http://www.2cto.com/kf/201411/350935.html

            张鸿洋的博客









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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值