当一个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的接口方法的多次、频繁的定义。

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

  1. Android平台下Dalvik层hook框架ddi的研究

    通过adbi,可以对native层的所有代码进行hook.但对于Android系统来说,这还远远不够,因为很多应用都还是在Dalvik虚拟机中运行的. 那么,有没有什么办法可以对Dalvik虚拟机中跑 ...

  2. 【转】手把手教你读取Android版微信和手Q的聊天记录(仅作技术研究学习)

    1.引言 特别说明:本文内容仅用于即时通讯技术研究和学习之用,请勿用于非法用途.如本文内容有不妥之处,请联系JackJiang进行处理!   我司有关部门为了获取黑产群的动态,有同事潜伏在大量的黑产群 ...

  3. 手把手教你读取Android版微信和手Q的聊天记录(仅作技术研究学习)

    1.引言 特别说明:本文内容仅用于即时通讯技术研究和学习之用,请勿用于非法用途.如本文内容有不妥之处,请联系JackJiang进行处理!   我司有关部门为了获取黑产群的动态,有同事潜伏在大量的黑产群 ...

  4. Android消息传递之EventBus 3.0使用详解

    前言: 前面两篇不仅学习了子线程与UI主线程之间的通信方式,也学习了如何实现组件之间通信,基于前面的知识我们今天来分析一下EventBus是如何管理事件总线的,EventBus到底是不是最佳方案?学习 ...

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

    http://www.unitymanual.com/thread-23491-1-1.html 今天,这篇文章其实是个老生常谈的问题咯,在网上类似的文章也比比皆是,在此我只是做个详细总结方便大家能够 ...

  6. 基于python的opcode优化和模块按需加载机制研究(学习与个人思路)(原创)

    基于python的opcode优化和模块按需加载机制研究(学习与思考) 姓名:XXX 学校信息:XXX 主用编程语言:python3.5 个人技术博客:http://www.cnblogs.com/M ...

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

     Android自复制传播APP原理学习(翻译) 1 背景介绍 论文链接:http://arxiv.org/abs/1511.00444 项目地址:https://github.com/Tribler ...

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

    今天,这篇文章其实是个老生常谈的问题咯,在网上类似的文章也比比皆是,在此我只是做个详细总结方便大家能够更好.更快的掌握,当然,如有不足的地方 欢迎指正!!! 相信大家在开发过程中,难免会保存一些文件在 ...

  9. Android中的SQLite使用学习

    Android中的SQLite使用学习 SQLite是非常流行的嵌入式关系型数据库,轻载, 速度快,而且是开源.在Android中,runtime提供SQLite,所以我们可以使用SQLite,而且是 ...

随机推荐

  1. RunLoop学习总结

    开始 很久之前就看了一次YY的文章,没看懂.后来又看了sunny的视频和叶孤城的直播的视频,找了很多资料,对RunLoop也越来越清晰,然后又看了两三次YY的文章,虽然还没完全看懂,不得不说写的非常好 ...

  2. 由基于qml,c++的串口调试工具浅谈qml与c++混合编程

    最近在做一个基于sim900 的串口通信工具,基于qml和c++来实现. 首先,对于串口,qt有自带的QSerialPort,可以实现同步,和异步通信,qt creator也有自带的例子,本例子是从其 ...

  3. Java网络蜘蛛/网络爬虫 Spiderman

    Spiderman - 又一个Java网络蜘蛛/爬虫 Spiderman 是一个基于微内核+插件式架构的网络蜘蛛,它的目标是通过简单的方法就能将复杂的目标网页信息抓取并解析为自己所需要的业务数据. 主 ...

  4. TCP和UDP的"保护消息边界" (经典)

    在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有 ...

  5. 西安力邦智能医疗&amp;可穿戴设备沙龙--第1期---苹果HealthKit、谷歌GoogleFit来袭,智能医疗要爆发吗?

    背    景: "可穿戴设备"成为2014的行业热点,从Google Glass到苹果iWatch, 越来越多的企业推出了包含眼镜.腕带.鞋等各种可穿戴设备,"可穿戴&q ...

  6. java操作Excel处理数字类型的精度损失问题验证

    java操作Excel处理数字类型的精度损失问题验证: 场景: CELL_TYPE_NUMERIC-->CELL_TYPE_STRING--->CELL_TYPE_NUMERIC POI版 ...

  7. oracle 11g 11.2.0.1 设置HuagePage导致TRC 变大 变多

    最近发现diag/..../trac/ 目录下  sid_ora_xxxx.trc 文件大小为11M 而且类似文件数量很大.导致占用了8G硬盘空间 另外个同事说他的DG没有这个问题. 都一样的系统和一 ...

  8. 大数据时代的 9 大Key-Value存储数据库

    在过去的十年中,计算世界已经改变.现在不仅在大公司,甚至一些小公司也积累了TB量级的数据.各种规模的组织开始有了处理大数据的需求,而目前关系型数据库在可缩放方面几乎已经达到极限. 一个解决方案是使用键 ...

  9. mysql设置root密码

    1.停止mysql服务.可以在windows服务管理器设置,运行->services.msc->停止mysql服务 2.跳过权限验证: mysqld -nt --skip-grant-ta ...

  10. TTimerThread和TThreadedTimer(都是通过WaitForSingleObject和CreateEvent来实现的)

    //////////////////////////////////////////////////// // // // ThreadedTimer 1.24 // // // // Copyrig ...