android平台中,EventBus研究学习

当一个Android应用功能越来越多的时候,app中各个部分之间通信,往往采用Observer的方式来进行,即注册----通知----注销的方式执行 各类控件经常需要根据某个状态来更新显示内容。这种场景常见的解决方式就是定义一个接口,需要关注该事件的控件来实现这个接口。 接口类:

public interface OnChangedListener {

void onDataChanged();

}

被观察者往往以如下形式实现:

public abstract class AbsHTTPRequest {
    private final WeakHashMap<OnChangedListener, Boolean> mListeners = new WeakHashMap<OnChangedListener, Boolean>();
    public interface OnChangedListener {
        void onDataChanged();
    }

    /*HTTP's response*/
    public abstract void onResponse();

    public final void addListener(OnChangedListener listener) {
        mListeners.put(listener, true);
    }

    public final void removeListener(OnChangedListener listener) {
        mListeners.remove(listener);
    }

    protected final void notifyDataChanged() {
        Set<OnChangedListener> keys = mListeners.keySet();
        if(keys != null) {
            Iterator<OnChangedListener> iterator = keys.iterator();
            while(iterator.hasNext()) {
                iterator.next().onDataChanged();
            }
        }
    }
}

具体的主题角色( 被观察者),实现方式如下:

public class LoginRequest extends AbsHTTPRequest implements OnChangedListener{
	public void onResponse(){
		addListener(this);
		notifyDataChanged();
	}

	@Override
	public void onDataChanged() {
		// TODO Auto-generated method stub
		System.out.println("LoginRequest");
	}
}

使用观察者模式有一个弊病就是部件之间的耦合度太高,所有的主题角色都需要实现同一个interface。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。如果主题角色被注册的observer越多,那么需要实现的interface也就越多,接口方法数量也就越多。

如何来进行解耦,让代码逻辑更清晰,可读性更强,是一个问题。

在Android中也有一个类似功能的开源库EventBus,可以很方便的帮助我们实现观察者模式,并且让各个组件之间的耦合性更低。

关于EventBus的讲解文章,网络业很多,这里推荐一篇,讲解比较详细的blog,http://www.cnblogs.com/angeldevil/p/3715934.html

对EventBus的认识,最好还是从demo入手,先易后难。首先知道如何使用,然后再深究源码,才能循序渐进,吃透其中的设计理念,便于日后的代码调试和模块重构。关于demo,网上有很多,可以自己去查收。

EventBus的使用有4个步奏:

1.定义事件类型:

public class MyEvent

2.注册订阅者:

EventBus.getDefault().register(this)

3.发送事件:

EventBus.getDefault().post(new MyEvent())

4.接收事件,处理

订阅者回调的函数。官方指导,函数名称以onEvent开头。

在EventBus模块中,有几个重要的概念,了解了这几个概念后,也就不难懂了。

Event:可以是任意类型的对象

Subscriber:订阅者,接收特定的

Publisher:发布者,用于通知Subscriber发送

EventType:onEvent函数中的参数,表示事件对象,用户自定义的。

Subscriber:订阅源,即调用register注册的对象,这个对象内包含onEvent成员函数。

SubscribMethod.java
final class SubscriberMethod {
    final Method method;  /*Method类型的method成员表示这个onEvent,即事件处理函数。同时也包含订阅源*/
    final ThreadMode threadMode;
    final Class<?> eventType; /*事件的对象,用户自定义Object*/
... ... ... ... ... ... ... ... ... ... ... ...
}

Subscription.java

final class Subscription {
    final Object subscriber;  /*订阅源Subscriber,即调用register注册的对象*/
    final SubscriberMethod subscriberMethod; /**/
    final int priority;
... ... ... ... ... ... ... ... ... ... ... ...
}

类EventBus中,有两个核心的成员

   /*EventType -> List<Subscription>,事件到订阅对象之间的映射*/
    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
  /* Subscriber -> List<EventType>,订阅对象到它订阅的的所有事件的映射关系*/
    private final Map<Object, List<Class<?>>> typesBySubscriber;

  注册流程:在调用register函数时,EventBus类有多个重载的register函数,但是作者更倾向于使用register(this);含有 多个参数的register函数中,明确标注了@deprecated,原创作者不建议使用。从代码:

    public void register(Object subscriber) {
        register(subscriber, DEFAULT_METHOD_NAME, false, 0);
    }

可以观察到,所有重载的register函数,都调用到了

    private synchronized void register(Object subscriber, String methodName, boolean sticky, int priority) {
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass(),methodName);
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod, sticky, priority);
        }
    }

其中注册函数register,默认参数DEFAULT_METHOD_NAME为函数名称"onEvent",在java放射机制中,所有的事件处理函数名称 统一为“onEvent”,仅仅参数不一致。onEvent的参数为用户自定义的对象。

注册时,使用SubscriberMethodFinder的对象,调用到findSubscriberMethods方法,获取到List<SubscriberMethod>。

数组对象Method[],调用getMethods()方法, 获取的是类的所有共有方法,这就包括自身的所有public方法,和从基类继承的、从接口实现的所有public方法。这也是为啥,我们的onEvent函数,要定义为public方法的原因哦。

在findSubscriberMethods函数中,进行如此频繁的遍历,就是为了找到List<SubscriberMethod>。 每一个订阅者,对应一个List<SubscriberMethod>,有多少onEvent函数,返回的List,就有多少个item。即查找订阅源内的事件处理方法,同时还会查到它的父类中的事件处理方法,返回list,交给

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority)

进行处理。

Event与Subscriber之间,是一对多的关系。即一个事件,可以被多个订阅者关注。

Subscriber与Event之间,也是一对多的关系。即一个订阅者,可以订阅多个事件。

subscribe方法,也就是将上述的那样的关系,进行理顺,合理的建立map的映射关系,主要做了这样几件事件。

a.根据SubscriberMethod中的EventType类型,将Subscribtion对象存放在subscriptionsByEventType中。建立EventType与Subscription的映射,一个事件可以有多个订阅者。

b.根据Subscriber将EventType存放在typesBySubscriber`中,建立Subscriber到EventType的映射,每个Subscriber可以订阅多个事件。

c.如果是Sticky类型的订阅者,直接向它发送上个保存的事件(如果有的话)。

通过Subscriber到EventType的映射,我们就可以很方便地使一个Subscriber取消接收事件,通过EventType到Sucscribtion的映射,可以方便地将相应的事件发送到它的每一个订阅者。

与Observer不同的是,使用EventBus,不同的被观察者,不需统一实现Observer中的interface方法,在上层代码中,也不需要逐一进行notify机制。通过Map进行订阅源与事件函数的对应关系,进行解耦,为其核心之处。

发送流程:

EventBus.getDefault().post(new EventType());参数为用户自定义的对象。最为简单的处理方式,实现事件发送。

当事件发送出去后,所有的订阅者,是如何调用其事件方法的呢?这个就需要遍历上文提到的subscriptionsByEventType的Map了。Post发送事件,入口为post函数:public void post(Object event),在postSingleEvent函数中个,有一个重要的处理函数:

    /** Finds all Class objects including super classes and interfaces. */
    private List<Class<?>> findEventTypes(Class<?> eventClass) {
        synchronized (eventTypesCache) {
            List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
            if (eventTypes == null) {
                eventTypes = new ArrayList<Class<?>>();
                Class<?> clazz = eventClass;
                while (clazz != null) {
                    eventTypes.add(clazz);
                    addInterfaces(eventTypes, clazz.getInterfaces());
                    clazz = clazz.getSuperclass();
                }
                eventTypesCache.put(eventClass, eventTypes);
            }
            return eventTypes;
        }
    }

其作用,就是把这个事件类的对象、实现的接口及父类的类对象存到一个List中返回,根据list中的eventTypes,遍历subscriptionsByEventType,获取订阅源对象,进行逐一的调用事件函数。

这里需要注意的是,当Post一个事件时,这个事件的父事件(事件类的父类事件)、接口事件也会被Post,所以如果订阅者接收Object类型的事件,即包含onEvent(Object object)事件函数,那么Subscriber就可以接收所有的事件。

通过本篇博文的了解,EventBus就是通过Map,建立订阅源与事件函数的对应关系,进行解耦,来规避Observer的接口方法的多次、频繁的定义。

时间: 2024-11-10 13:33:02

android平台中,EventBus研究学习的相关文章

Android gc垃圾回收研究学习

尊重个人劳动成果,转载请注明出处:http://blog.csdn.net/hnulwt/article/details/44903331 文中很多内容说到了JVM,我想通过研究学习JVM来达到认识DVM的目的.为了严谨,查询了一下 JVM和DVM的不同点 1.Dalvik 和标准 Java 虚拟机(JVM)的首要差别 Dalvik 基于寄存器,而 JVM 基于栈.基于寄存器的虚拟机对于更大的程序来说,在它们编译的时候,花费的时间更短. 2.Dalvik 和 Java 字节码的区别 Dalvik

Android平台上的Linphone学习(一)

Linphone: 适用于很多平台(Windows, Mac OS, Android)的VOIP电话工具, 基于标准SIP协议. Linphone-android: Android平台上的Linphone. Linphone-android的工程中,一部分是Java实现的功能框架,另一部分是JNI实现的linphone动态库. 学习过程可以分两步: (1)熟悉Java层框架; (2)阅读linphone动态库的C源码. 环境配置可以分三部分: (1)下载已经编译好的Linphone-androi

Android自复制传播APP原理学习(翻译)

 Android自复制传播APP原理学习(翻译) 1 背景介绍 论文链接:http://arxiv.org/abs/1511.00444 项目地址:https://github.com/Tribler/self-compile-Android 吃完晚饭偶然看到这篇论文,当时就被吸引了,马上翻译总结了一下.如有错误欢迎斧正. 该论文的研究出发点比较高大上这里我们就不多说了,简而言之就是想通过移动设备来实现一个自组网,在发生灾难的时候,手机之间能够自动传输关键数据,减少损失.整个目标通过设计一个能够

(转) unity 在移动平台中,文件操作路径详解

http://www.unitymanual.com/thread-23491-1-1.html 今天,这篇文章其实是个老生常谈的问题咯,在网上类似的文章也比比皆是,在此我只是做个详细总结方便大家能够更好.更快的掌握,当然,如有不足的地方 欢迎指正!!! 相信大家在开发过程中,难免会保存一些文件在客户端进行本地化操作.如:配置文件,状态文件,Assetbundle文件等等... 最近总有人问我:1.保存了一个xml在客户端,能读取里面的数据,可是不能修改,甚至一修改就报错...2.我在电脑上操作

一样的Android,不一样的学习

这几年,Android开始慢慢流行起来,很多项目也开始涉及这部分内容,所以学习Android也就变的很有意义了. 学什么 学习Android应该学什么,很多人有不同的见解.一般程序员可能只是学习Android的UI控件和Android的API使用等.高级程序员可能会研究一下Android的FrameWork和Android的实现原理.相对于这两者,顶级的程序员会深入的了解Android的层次架构和设计思想并将这些架构和设计思想加以改造.优化之后应用在其他方面.你会怎么做呢? Android层次架

Android中关于JNI 的学习(四)简单的例子,温故而知新

在第零篇文章简单地介绍了JNI编程的模式之后,后面两三篇文章,我们又针对JNI中的一些概念做了一些简单的介绍,也不知道我到底说的清楚没有,但相信很多童鞋跟我一样,在刚开始学习一个东西的时候,入门最好的方式就是一个现成的例子来参考,慢慢研究,再学习概念,再回过来研究代码,加深印象,从而开始慢慢掌握. 今天我们就再来做一个小Demo,这个例子会比前面稍微复杂一点,但是如果阅读过前面几篇文章的话,理解起来也还是很简单的.很多东西就是这样,未知的时候很可怕,理解了就很简单了. 1)我们首先定义一个Jav

Android WebView简要介绍和学习计划

我们通常会在App的UI中嵌入WebView,用来实现某些功能的动态更新.在4.4版本之前,Android WebView基于WebKit实现.不过,在4.4版本之后,Android WebView就换成基于Chromium的实现了.基于Chromium实现,使得WebView可以更快更流畅地显示网页.本文接下来就介绍Android WebView基于Chromium的实现原理,以及制定学习计划. 通过前面几个系列文章的学习,我们知道,Chromium的实现是相当复杂的.这种复杂可以体现在编译出

虚拟化Xen平台中,Dom0和DomU之间发送网络数据时各个部分所运行时间

12年研究Xen的时候,曾经写过很多报告,当时考虑到保密,不能随便发布.现在Xen已经被KVM干的快不行了,发出来供大家参考. 关于xennet_start_xmit <-dev_hard_start_xmit调用函数(notify_remote_via_irq <-xennet_start_xmit)之间时间(0.085313)很长的解释:前端放入请求后,需要根据notify来表明,是否需要向后端发送事件请求.如果后端正在处理请求队列,就不需要向后端发送事件通知(notify=false).

unity 在移动平台中,文件操作路径详解

转载自:http://www.unitymanual.com/thread-23491-1-1.html 今天,这篇文章其实是个老生常谈的问题咯,在网上类似的文章也比比皆是,在此我只是做个详细总结方便大家能够更好.更快的掌握,当然,如有不足的地方 欢迎指正!!! 相信大家在开发过程中,难免会保存一些文件在客户端进行本地化操作.如:配置文件,状态文件,Assetbundle文件等等... 最近总有人问我:1.保存了一个xml在客户端,能读取里面的数据,可是不能修改,甚至一修改就报错...2.我在电