转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/41096639 。本文出自:【张鸿洋的博客】

1、概述

关于Eventbus的介绍。前面已经有两篇:Android EventBus实战 没听过你就out了和Android EventBus源代码解析 带你深入理解EventBus 。 假设你觉得还有问题,没关系,接下来我带大家手把手打造从无到有的编写这种框架~~~

首先我们回想一下,这玩意就是在register时,扫描类中复合命名规范的方法,存到一个map,然后post的时候。查找到匹配的方法,反射调用;好,那么依据这一句话。我们就開始编写框架之旅~~~

2、依旧是原来的配方

下面出现的实例代码和Android EventBus实战 没听过你就out了基本一致。所以我就贴出部分

1、ItemListFragment

package com.angeldevil.eventbusdemo;

import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView; import com.angeldevil.eventbusdemo.Event.ItemListEvent;
import com.zhy.eventbus.EventBus; public class ItemListFragment extends ListFragment
{ @Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Register
EventBus.getInstatnce().register(this);
} @Override
public void onDestroy()
{
super.onDestroy();
// Unregister
EventBus.getInstatnce().unregister(this);
} @Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
// 开启线程载入列表
new Thread()
{
public void run()
{
try
{
Thread.sleep(2000); // 模拟延时
// 公布事件,在后台线程发的事件
EventBus.getInstatnce().post(new ItemListEvent(Item.ITEMS));
} catch (InterruptedException e)
{
e.printStackTrace();
}
};
}.start();
} public void onEventUI(ItemListEvent event)
{
setListAdapter(new ArrayAdapter<Item>(getActivity(),
android.R.layout.simple_list_item_activated_1,
android.R.id.text1, event.getItems()));
} @Override
public void onListItemClick(ListView listView, View view, int position,
long id)
{
super.onListItemClick(listView, view, position, id);
EventBus.getInstatnce().post(getListView().getItemAtPosition(position));
} }

2、ItemDetailFragment

package com.angeldevil.eventbusdemo;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView; import com.zhy.eventbus.EventBus; public class ItemDetailFragment extends Fragment
{ private TextView tvDetail; @Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// register
EventBus.getInstatnce().register(this);
} @Override
public void onDestroy()
{
super.onDestroy();
// Unregister
EventBus.getInstatnce().unregister(this);
} /** List点击时会发送些事件,接收到事件后更新详情 */
public void onEventUI(Item item)
{
if (item != null)
tvDetail.setText(item.content);
} @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_item_detail,
container, false);
tvDetail = (TextView) rootView.findViewById(R.id.item_detail);
return rootView;
}
}

能够看到。我们在ItemListFragment里面使用了:

EventBus.getInstatnce().post(new ItemListEvent(Item.ITEMS));去公布了一个事件。然后更新了我们的列表;

点击Item的时候,使用EventBus.getInstatnce().post(getListView().getItemAtPosition(position));公布了一个事件,更新了我们的ItemDetailFragment的列表;

效果:

效果图和之前的一摸一样~~~

可是请注意,如今我们用的是EventBus.getInstatnce()。并发是EventBus.getDefault();而且看下包名import com.zhy.eventbus.EventBus;

我想你应该明确了,这是我们自己写的类来实现的~~~~

好了,接下来就带大家一起实现这个类~~

ps :以上代码和效果图。全然是为了博客的完整性。勿见怪~~

3、无中生有

1、getInstance

我们这里为了方便,直接简单粗暴的使用恶汉模式创建单例:

	private static EventBus eventBus = new EventBus();

	public static EventBus getInstatnce()
{
return eventBus;
} private EventBus()
{
mHandler = new Handler(Looper.getMainLooper());
}

然后在构造方法中初始化了一个mHandler,没错,它就是用来在处理在UI线程调用方法的。

接下来看register

2、register

/*
* 我们的强大的map。存储我们的方法
*/
private static Map<Class, CopyOnWriteArrayList<SubscribeMethod>> mSubscribeMethodsByEventType = new HashMap<Class, CopyOnWriteArrayList<SubscribeMethod>>(); public void register(Object subscriber)
{ Class clazz = subscriber.getClass();
Method[] methods = clazz.getDeclaredMethods(); CopyOnWriteArrayList<SubscribeMethod> subscribeMethods = null;
/**
* 遍历全部方法
*/
for (Method method : methods)
{
String methodName = method.getName();
/**
* 推断方法是否以onEvent的开头
*/
if (methodName.startsWith("onEvent"))
{
SubscribeMethod subscribeMethod = null;
// 方法命中提前在什么线程执行。默认在UI线程
String threadMode = methodName.substring("onEvent".length());
ThreadMode mode = ThreadMode.UI; Class<? >[] parameterTypes = method.getParameterTypes(); // 參数的个数为1
if (parameterTypes.length == 1)
{
Class<?> eventType = parameterTypes[0]; synchronized (this)
{ if (mSubscribeMethodsByEventType.containsKey(eventType))
{
subscribeMethods = mSubscribeMethodsByEventType
.get(eventType);
} else
{
subscribeMethods = new CopyOnWriteArrayList<SubscribeMethod>();
mSubscribeMethodsByEventType.put(eventType,
subscribeMethods);
}
} if (threadMode.equals("Async"))
{
mode = ThreadMode.Async;
}
// 提取出method,mode,方法所在类对象。存数的类型封装为SubscribeMethod
subscribeMethod = new SubscribeMethod(method, mode,
subscriber);
subscribeMethods.add(subscribeMethod);
}
} }
} enum ThreadMode
{
UI, Async
} class SubscribeMethod
{
Method method;
ThreadMode threadMode;
Object subscriber; public SubscribeMethod(Method method, ThreadMode threadMode,
Object subscriber)
{
this.method = method;
this.threadMode = threadMode;
this.subscriber = subscriber;
} }

能够看到我们使用了一个Map存储全部的方法。key为參数的类型class;value为CopyOnWriteArrayList<SubscribeMethod>

这里我们封装了一个SubscribeMethod,这个里面存储了我们须要执行方法的全部參数,毕竟我们执行时。须要该方法,该方法所在的对象。以及在什么线程执行;三个对象足以。当然也缺一不可了~~

register里面,我们遍历该类的全部方法,找到onEvent开头的,封装成SubscribeMethod。存在Map里面。当然了,一个參数类型相应非常多方法,所以value是个CopyOnWriteArrayList。

扫描完毕。我们就完毕了将方法的存储。

另一点,我们这里默认在UI线程执行,假设方法是onEventAsync则觉得在子线程执行,我们也仅仅支持这两种模式,简化一点~

3、post

private static ThreadLocal<PostingThread> mPostingThread = new ThreadLocal<PostingThread>()
{
@Override
public PostingThread get()
{
return new PostingThread();
}
}; public void post(Object eventTypeInstance)
{
//拿到该线程中的PostingThread对象
PostingThread postingThread = mPostingThread.get();
postingThread.isMainThread = Looper.getMainLooper() == Looper
.myLooper();
//将事件增加事件队列
List<Object> eventQueue = postingThread.mEventQueue;
eventQueue.add(eventTypeInstance);
//防止多次调用
if (postingThread.isPosting)
{
return;
}
postingThread.isPosting = true;
//取出全部事件进行调用
while (!eventQueue.isEmpty())
{
Object eventType = eventQueue.remove(0);
postEvent(eventType, postingThread);
}
postingThread.isPosting = false; }

我们这里学习了源代码,也搞了个当前线程中的变量,存储了一个事件队列以及事件的状态;

class PostingThread
{
List<Object> mEventQueue = new ArrayList<Object>();
boolean isMainThread;
boolean isPosting;
}

终于公布的事件先增加到事件队列。然后再取出来调用postEvent

private void postEvent(final Object eventType, PostingThread postingThread)
{
CopyOnWriteArrayList<SubscribeMethod> subscribeMethods = null;
synchronized (this)
{
subscribeMethods = mSubscribeMethodsByEventType.get(eventType
.getClass());
} for (final SubscribeMethod subscribeMethod : subscribeMethods)
{ if (subscribeMethod.threadMode == ThreadMode.UI)
{
if (postingThread.isMainThread)
{
invokeMethod(eventType, subscribeMethod);
} else
{
mHandler.post(new Runnable()
{
@Override
public void run()
{
invokeMethod(eventType, subscribeMethod);
}
});
}
} else
{
new AsyncTask<Void, Void, Void>()
{ @Override
protected Void doInBackground(Void... params)
{
invokeMethod(eventType, subscribeMethod);
return null;
}
}; } } }

postEvent也非常easy,直接依据參数类型,去map改到该方法,依据其threadMode。假设在UI线程。则推断当前线程。假设是UI线程,直接调用。否则通过handler执行;

假设非UI线程。这里我们直接开启了一个Thread去执行;

invokeMethod非常easy。就是反射调用方法了~

private void invokeMethod(Object eventType, SubscribeMethod subscribeMethod)
{
try
{
subscribeMethod.method
.invoke(subscribeMethod.subscriber, eventType);
} catch (Exception e)
{
e.printStackTrace();
}
}

4、unregister

public void unregister(Object subscriber)
{
Class clazz = subscriber.getClass();
Method[] methods = clazz.getDeclaredMethods(); List<SubscribeMethod> subscribeMethods = null; for (Method method : methods)
{
String methodName = method.getName(); if (methodName.startsWith("onEvent"))
{
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1)
{
synchronized (this)
{
mSubscribeMethodsByEventType.remove(parameterTypes[0]);
}
}
}
} }

unregister时。由于我们没有存不论什么的辅助状态,我们仅仅能再去遍历了方法了~~只是通过这个。也能反应出EventBus内部好几个Map的作用了~~

而且。我们也不支持一些状态的查询,还是由于我们没有存一些辅助状态,比如isRegister等等。

到此,我们的EventBus就写好了。100多行代码。肯定没有EventBus健壮,主要目的还是学习人家的思想,经过自己写了这么个类,我相信对于EventBus的理解就更深刻了~面试的时候,恨不得拿仅仅笔写给面试官看,哈哈~~

5、EventBus最佳实践

前面的文章,非常多朋友问。假设我多个方法參数都一样,岂不是post一个此參数,会多个方法调用;而此时我想调用指定的方法怎么办?

还有。项目中会有非常多地方去接收List參数,而List<T>中的泛型是不一致的,所以也可能post(List)时。会调用非常多方法。造成出错。

的确。上述,不加处理肯定会出现;

可是,推荐大家在使用EventBus的时候。创建一个事件类,把你的每个參数(或者可能发生冲突的參数),封装成一个类:

比如:

public class Event
{
public static class UserListEvent
{
public List<User> users ;
}
public static class ItemListEvent
{
public List<Item> items;
} }

这种话。就不会发生什么调用冲突了~~

源代码点击下载

我建了一个QQ群,方便大家交流。群号:55032675

----------------------------------------------------------------------------------------------------------

博主部分视频已经上线,假设你不喜欢枯燥的文本。请猛戳(初录。期待您的支持):

1、高仿微信5.2.1主界面及消息提醒

2、高仿QQ5.0側滑

3、Android智能机器人“小慕”的实现

Android 框架炼成 教你怎样写组件间通信框架EventBus的更多相关文章

  1. Android 框架炼成 教你如何写组件间通信框架EventBus

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/41096639 ,本文出自:[张鸿洋的博客] 1.概述 关于Eventbus的介绍 ...

  2. Android组件间通信库EventBus学习

    项目地址:   https://github.com/greenrobot/EventBus EventBus主要特点 1. 事件订阅函数不是基于注解(Annotation)的,而是基于命名约定的,在 ...

  3. Android 组件间通信--事件驱动

    在android中,组件间通信常用的方式: 1.使用广播机制:在主页面中监听特定的广播事件,进行业务逻辑的操作,其他页面只需要根据需求发送广播即可 例如:常用app结构中,左边通常为菜单栏,点击菜单栏 ...

  4. Android组件内核之间组件间通信方案(四)下篇

    阿里P7Android高级架构进阶视频免费学习请点击:https://space.bilibili.com/474380680本篇文章将继续从以下两个内容来介绍通信方案: [ViewModel 与 V ...

  5. Android Service、IntentService,Service和组件间通信

    Service组件 Service 和Activity 一样同为Android 的四大组件之一,并且他们都有各自的生命周期,要想掌握Service 的用法,那就要了解Service 的生命周期有哪些方 ...

  6. Android 框架练成 教你打造高效的图片加载框架(转)

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/41874561,本文出自:[张鸿洋的博客] 1.概述 优秀的图片加载框架不要太多, ...

  7. Android 框架练成 教你打造高效的图片加载框架

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/41874561,本文出自:[张鸿洋的博客] 1.概述 优秀的图片加载框架不要太多, ...

  8. 比最差的API(ETW)更差的API(LTTng)是如何炼成的, 谈如何写一个好的接口

    最近这几天在帮柠檬看她的APM系统要如何收集.Net运行时的各种事件, 这些事件包括线程开始, JIT执行, GC触发等等. .Net在windows上(NetFramework, CoreCLR)通 ...

  9. Android组件内核之组件间通信方案(四)上篇

    阿里P7Android高级架构进阶视频免费学习请点击:https://space.bilibili.com/474380680本篇文章将先从以下三个内容来介绍通信方案: [Activity与Fragm ...

随机推荐

  1. Android KeyCode(官方)

    Constants public static final int ACTION_DOWN Added in API level 1 getAction() value: the key has be ...

  2. Axure基础系列教程

     Axure rp 6.5的软件安装.汉化与注册  认识Axure的软件界面 生成网页原型的三种方法 如何关闭IE浏览器在生成原型时候的安全警告 在chrome中使用axure生成原型的问题 站点地图 ...

  3. tomcat 后台启动设置

    如果你环境变量配置对了且保证tomcat的bin目录下有如下三个文件,则你只需在cmd中运行 service install tomcat即可将tomcat添加的服务项中,然后在设置开机自动启动,则以 ...

  4. Web前端性能优化的14条规则

    1.减少Http请求 使用图片地图 使用CSS Sprites 合并JS和CSS文件 这个是由于浏览器对同一个host有并行下载的限制,http请求越多,总体下载速度越慢 2.使用CDN(内容发布网络 ...

  5. Swift - 给表格UITableView添加索引功能(快速定位)

    像iOS中的通讯录,通过点击联系人表格右侧的字母索引,我们可以快速定位到以该字母为首字母的联系人分组.   要实现索引,我们只需要两步操作: (1)实现索引数据源代理方法 (2)响应点击索引触发的代理 ...

  6. Swift - 使用TableView的静态单元格进行页面布局

    通过使用静态单元格的列表,我们可以很方便的进行页面布局.下面通过一个“添加任务页面”来进行演示. 效果图如下: 实现步骤: 1,在storyboard中拖入一个TableViewController, ...

  7. ORA-00942:表或视图不存在(低级错误)

    在好多时候.调试PL/SQL对象时会报.ORA-00942 看看错误原因吧: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamFjc29uX2JhaQ== ...

  8. nohup命令与&区别,jobs,fg,bg,Ctrl-Z、Ctrl-C、Ctrl-D

    &方式: Unix/Linux下一般想让某个程序在后台运行,很多都是使用 & 在程序结尾来让程序自动运行.比如我们要运行mysql在后台:          /usr/local/my ...

  9. jquery mobile 对手势触控提供了如下几个事件监听:

    jquery mobile 对手势触控提供了如下几个事件监听: 复制代码代码如下: tap  当用户点屏幕时触发taphold 当用户点屏幕且保持触摸超过1秒时触发swipe 当页面被垂直或者水平拖动 ...

  10. 在 SSIS package 中使用FTP

    在ssis 包中使用FTP 实际上很简单, 直接拿一个FTP控制流(FTP 任务) ,配置一下FTP 服务器就可以了, 但是当我想在SQL Server Job 中使用这个功能时却报了个错(如下), ...