欢迎转载,转载请注明出处:juejin.im/post/5cbe81…java
因为上一篇文章《开源一个自用的Android IM库,基于Netty+TCP+Protobuf实现。》获得了不错的反响,激发了写做的兴趣,趁着时间空闲,决定继续写一些文章,如下这篇,是一个自定义的Android事件分发中心库,实现相似系统广播、EventBus、RxBus的事件发布-订阅功能,后续有时间,会继续完善以前的NettyChat库,包括加入WebSocket协议实现、UDP协议实现以及你们须要的UI页面的封装(包含会话列表页、消息列表页等),敬请期待。android
本文的CEventCenter基于对象池及接口回调实现,主要解决在Activity/Fragment/Service之间的消息事件传递问题,因为做者水平有限,文中有不对之处欢迎批评与指正。git
老规矩,先来看看效果: github
接下来,让咱们进入正题。数组
首先,BroadcastReceiver是重量级的、消耗资源较多的方式。其次,咱们都知道,onReceive()方法是在主线程运行的,执行时间不能超过10秒,不然会致使ANR。那么,你们可能会有疑惑,直接在onReceive()中开启一个子线程处理耗时任务不就能够了吗?这种方式,不能说不行,只能说并不可靠。Receiver只在onReceive方法执行时是激活状态,只要onReceive一返回,Receiver就再也不是激活状态了。因为activity可能会被用户退出,Broadcast Receiver的生命周期自己就很短,可能出现的状况是:
在子线程尚未结束的状况下,Activity已经被用户退出了,或者BroadcastReceiver已经结束了。在Activity已经退出、BroadcastReceiver已经结束的状况下,此时它们所在的进程就变成了空进程(没有任何活动组件的进程),系统须要内存时可能会优先终止该进程。若是宿主进程被终止,那么该进程内的全部子线程也会被停止,这样就可能致使子线程没法执行完成。
以上摘自:为何不能在BroadcastReceiver中开启子线程bash
其实我也有在用,记得在17年初的时候,咱们当时在作一个直播项目,其中的消息事件传递,就是用的RxBus,当时是这样的:观众给主播送礼,是经过im给服务端发送消息,服务端收到送礼消息后,处理送礼的逻辑,而后给客户端返回送礼的状态消息,客户端收到消息后,经过RxBus把消息传递到Activity(其实这里不论是经过im仍是http接口请求,都存在相同的问题)。在压测的时候,是每一个50ms送一个礼物,很大的几率会出现一个bug,就是下图这个: ide
目前EventBus最新的版本应该是3.x,这里面就有一个比较坑爹的设计:事件只能经过事件的类名来区分。
这至少带来了3大问题:函数
以上摘自:EventBus的缺点及改进升级,原做者也提供了改进的思路,有兴趣能够进去看看,固然了,EventBus的线程模型设计和粘性事件的支持是很是好的。post
上面有提到,CEventCenter是基于对象池及接口回调实现的,那么,什么是对象池?其实你们应该都使用过OkHttp,了解过源码的应该都知道,OkHttp源码里面,就有一种叫作链接池的东西,而对象池,跟链接池相似。
在java中,对象的生命周期包括对象建立、对象使用,对象消失三个时间段,其中对象的使用是对象真正须要存活的时间,很差修改,该用的时候还得使用啊。对象的建立和消失就得好好控制下了。对象的建立是比较费时间的,也许感受不到,比如一个赋值操做int i=1,也是须要耗时的,在好比构造一个对象,一个数组就更加消耗时间。再说对象的消除,在 java 里面使用GC来进行对象回收,其实也是须要对对象监控每个运行状态,包括引用,赋值等。在Full GC的时候,会暂停其余操做,独占CPU。因此,咱们须要控制对象的建立数量,也不要轻易的让对象消失,让他的复用更加充分。
以上摘自:java 对象池技术学习
废话很少说,直接开始吧。
首先,定义一个对象池中使用的对象接口:
PooledObject.java
package com.freddy.event;
/**
* <p>@ProjectName: CEventCenter</p>
* <p>@ClassName: PooledObject.java</p>
* <p>@PackageName: com.freddy.event</p>
* <b>
* <p>@Description: 对象池中的对象要求实现PooledObject接口</p>
* </b>
* <p>@author: FreddyChen</p>
* <p>@date: 2019/04/25 16:59</p>
* <p>@email: chenshichao@outlook.com</p>
*/
public interface PooledObject {
/**
* 恢复到默认状态
*/
void reset();
}
复制代码
而后,定义一个事件模型,也就是须要传递的消息事件对象:
CEvent.java
package com.freddy.event;
/**
* <p>@ProjectName: CEventCenter</p>
* <p>@ClassName: CEvent.java</p>
* <p>@PackageName: com.freddy.event</p>
* <b>
* <p>@Description: 事件模型</p>
* </b>
* <p>@author: FreddyChen</p>
* <p>@date: 2019/04/25 17:24</p>
* <p>@email: chenshichao@outlook.com</p>
*/
public class CEvent implements PooledObject {
private String topic; // 主题
private int msgCode; // 消息类型
private int resultCode; // 预留参数
private Object obj; // 回调返回数据
public CEvent() {
}
public CEvent(String topic, int msgCode, int resultCode, Object obj) {
this.topic = topic;
this.msgCode = msgCode;
this.resultCode = resultCode;
this.obj = obj;
}
public String getTopic() {
return topic;
}
public void setTopic(String topic) {
this.topic = topic;
}
public int getMsgCode() {
return msgCode;
}
public void setMsgCode(int msgCode) {
this.msgCode = msgCode;
}
public int getResultCode() {
return resultCode;
}
public void setResultCode(int resultCode) {
this.resultCode = resultCode;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
/**
* 恢复到默认状态
*/
@Override
public void reset() {
this.topic = null;
this.msgCode = 0;
this.resultCode = 0;
this.obj = null;
}
}
复制代码
接下来,自定义一个对象池:
ObjectPool.java
package com.freddy.event;
/**
* <p>@ProjectName: CEventCenter</p>
* <p>@ClassName: ObjectPool.java</p>
* <p>@PackageName: com.freddy.event</p>
* <b>
* <p>@Description: 自定义的对象池</p>
* </b>
* <p>@author: FreddyChen</p>
* <p>@date: 2019/04/25 17:30</p>
* <p>@email: chenshichao@outlook.com</p>
*/
public abstract class ObjectPool<T extends PooledObject> {
private T[] mContainer;// 对象容器
private final Object LOCK = new Object();// 对象锁
private int length;// 每次返回对象都放到数据末端,length表示前面可用对象数
public ObjectPool(int capacity) {
mContainer = createObjPool(capacity);
}
/**
* 建立对象池
*
* @param capacity 最大限度容量
* @return 对象池
*/
protected abstract T[] createObjPool(int capacity);
/**
* 建立一个新的对象
*
* @return
*/
protected abstract T createNewObj();
/**
* 从对象池中捞出一个对象,若是池已满,会从新建立一个对象
*
* @return
*/
public final T get() {
// 先从池中找到空闲的对象,若是没有,则从新建立一个对象
T obj = findFreeObject();
if (null == obj) {
obj = createNewObj();
} else {
// 清除对象状态
obj.reset();
}
return obj;
}
/**
* 从池中找到空闲的对象
*
* @return
*/
private T findFreeObject() {
T obj = null;
synchronized (LOCK) {
if (length > 0) {
--length;
obj = mContainer[length];
// 赋值完成后,释放资源
mContainer[length] = null;
}
}
return obj;
}
/**
* 把对象放回池里面
*
* @param obj 须要放回对象池的对象
*/
public final void returnObj(T obj) {
synchronized (LOCK) {
int size = mContainer.length;
if (length < size) {
mContainer[length] = obj;
length++;
}
}
}
}
复制代码
而后,自定义一个事件对象池,继承自定义对象池:
CEventPool.java
package com.freddy.event;
/**
* <p>@ProjectName: CEventCenter</p>
* <p>@ClassName: CEventObjPool.java</p>
* <p>@PackageName: com.freddy.event</p>
* <b>
* <p>@Description: 事件对象池</p>
* </b>
* <p>@author: FreddyChen</p>
* <p>@date: 2019/04/25 17:45</p>
* <p>@email: chenshichao@outlook.com</p>
*/
public class CEventObjPool extends ObjectPool<CEvent> {
public CEventObjPool(int capacity) {
super(capacity);
}
@Override
protected CEvent[] createObjPool(int capacity) {
return new CEvent[capacity];
}
@Override
protected CEvent createNewObj() {
return new CEvent();
}
}
复制代码
还有事件监听器:
I_CEventListener.java
package com.freddy.event;
/**
* <p>@ProjectName: CEventCenter</p>
* <p>@ClassName: I_CEventListener.java</p>
* <p>@PackageName: com.freddy.event</p>
* <b>
* <p>@Description: 事件监听器</p>
* </b>
* <p>@author: FreddyChen</p>
* <p>@date: 2019/04/25 17:52</p>
* <p>@email: chenshichao@outlook.com</p>
*/
public interface I_CEventListener {
/**
* 事件回调函数
* <b>注意:</b><br />
* 若是 obj 使用了对象池,<br />
* 那么事件完成后,obj即自动回收到对象池,请不要再其它线程继续使用,不然可能会致使数据不正常
* @param topic
* @param msgCode
* @param resultCode
* @param obj
*/
void onCEvent(String topic, int msgCode, int resultCode, Object obj);
}
复制代码
最后,就是咱们的主角了,事件分发中心:
CEventCenter.java
package com.freddy.event;
import android.text.TextUtils;
import android.util.Log;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
/**
* <p>@ProjectName: CEventCenter</p>
* <p>@ClassName: CEventCenter.java</p>
* <p>@PackageName: com.freddy.event</p>
* <b>
* <p>@Description: 类描述</p>
* </b>
* <p>@author: FreddyChen</p>
* <p>@date: 2019/04/25 17:48</p>
* <p>@email: chenshichao@outlook.com</p>
*/
public class CEventCenter {
private static final String TAG = CEventCenter.class.getSimpleName();
/**
* 监听器列表,支持一对多存储
*/
private static final HashMap<String, Object> LISTENER_MAP = new HashMap<>();
/**
* 监听器列表锁
*/
private static final Object LISTENER_LOCK = new Object();
/**
* 事件对象池
*/
private static final CEventObjPool POOL = new CEventObjPool(5);
/**
* 注册/注销监听器
*
* @param toBind true注册 false注销
* @param listener 监听器
* @param topic 单个主题
*/
public static void onBindEvent(boolean toBind, I_CEventListener listener, String topic) {
onBindEvent(toBind, listener, new String[]{topic});
}
/**
* 注册/注销监听器
*
* @param toBind true注册 false注销
* @param listener 监听器
* @param topics 多个主题
*/
public static void onBindEvent(boolean toBind, I_CEventListener listener, String[] topics) {
if (toBind) {
registerEventListener(listener, topics);
} else {
unregisterEventListener(listener, topics);
}
}
/**
* 注册监听器
*
* @param listener 监听器
* @param topic 单个主题
*/
public static void registerEventListener(I_CEventListener listener, String topic) {
registerEventListener(listener, new String[]{topic});
}
/**
* 注册监听器
*
* @param listener 监听器
* @param topics 多个主题
*/
public static void registerEventListener(I_CEventListener listener, String[] topics) {
if (null == listener || null == topics) {
return;
}
synchronized (LISTENER_LOCK) {
for (String topic : topics) {
if (TextUtils.isEmpty(topic)) {
continue;
}
Object obj = LISTENER_MAP.get(topic);
if (null == obj) {
// 尚未监听器,直接放到Map集合
LISTENER_MAP.put(topic, listener);
} else if (obj instanceof I_CEventListener) {
// 有一个监听器
I_CEventListener oldListener = (I_CEventListener) obj;
if (listener == oldListener) {
// 去重
continue;
}
LinkedList<I_CEventListener> list = new LinkedList<>();
list.add(oldListener);
list.add(listener);
LISTENER_MAP.put(topic, list);
} else if (obj instanceof List) {
// 有多个监听器
LinkedList<I_CEventListener> listeners = (LinkedList<I_CEventListener>) obj;
if (listeners.indexOf(listener) >= 0) {
// 去重
continue;
}
listeners.add(listener);
}
}
}
}
/**
* 注销监听器
*
* @param listener 监听器
* @param topic 单个主题
*/
public static void unregisterEventListener(I_CEventListener listener, String topic) {
unregisterEventListener(listener, new String[]{topic});
}
/**
* 注销监听器
*
* @param listener 监听器
* @param topics 多个主题
*/
public static void unregisterEventListener(I_CEventListener listener, String[] topics) {
if (null == listener || null == topics) {
return;
}
synchronized (LISTENER_LOCK) {
for (String topic : topics) {
if (TextUtils.isEmpty(topic)) {
continue;
}
Object obj = LISTENER_MAP.get(topic);
if (null == obj) {
continue;
} else if (obj instanceof I_CEventListener) {
// 有一个监听器
if (obj == listener) {
LISTENER_MAP.remove(topic);
}
} else if (obj instanceof List) {
// 有多个监听器
LinkedList<I_CEventListener> listeners = (LinkedList<I_CEventListener>) obj;
listeners.remove(listener);
}
}
}
}
/**
* 同步分发事件
*
* @param topic 主题
* @param msgCode 消息类型
* @param resultCode 预留参数
* @param obj 回调返回数据
*/
public static void dispatchEvent(String topic, int msgCode, int resultCode, Object obj) {
if (!TextUtils.isEmpty(topic)) {
CEvent event = POOL.get();
event.setTopic(topic);
event.setMsgCode(msgCode);
event.setResultCode(resultCode);
event.setObj(obj);
dispatchEvent(event);
}
}
/**
* 同步分发事件
*
* @param event
*/
public static void dispatchEvent(CEvent event) {
// 没有监听器,直接跳出代码,无需执行如下代码
if (LISTENER_MAP.size() == 0) {
return;
}
if (null != event && !TextUtils.isEmpty(event.getTopic())) {
String topic = event.getTopic();
// 通知事件监听器处理事件
I_CEventListener listener = null;
LinkedList<I_CEventListener> listeners = null;
synchronized (LISTENER_LOCK) {
Log.d(TAG, "dispatchEvent | topic = " + topic + "\tmsgCode = " + event.getMsgCode()
+ "\tresultCode = " + event.getResultCode() + "\tobj = " + event.getObj());
Object obj = LISTENER_MAP.get(topic);
if (obj == null) {
return;
}
if (obj instanceof I_CEventListener) {
listener = (I_CEventListener) obj;
} else if (obj instanceof LinkedList) {
listeners = (LinkedList<I_CEventListener>) ((LinkedList) obj).clone();
}
}
// 分发事件
if (null != listener) {
listener.onCEvent(topic, event.getMsgCode(), event.getResultCode(), event.getObj());
} else if (null != listeners && listeners.size() > 0) {
for (I_CEventListener l : listeners) {
l.onCEvent(topic, event.getMsgCode(), event.getResultCode(), event.getObj());
}
}
// 把对象放回池里面
POOL.returnObj(event);
}
}
}
复制代码
代码比较简单,就是注册监听器->分发事件->事件回调响应->注销监听器四个步骤,支持一对1、一对多发布主题事件,事件分发完毕后,把对象放回对象池里面,便于对象复用。
使用方法,拿Activity举例吧:
咱们来看看运行效果:
这篇文章比较简单,因为以前的文章贴了大量的图片,致使可能加载过慢,并且在手机上看代码截图不是那么方便,因此这篇文章大部分换成了直接贴源码的方式,方便你们阅读。这个库实现的功能比较简单,因此源码也能所有贴上来了,若是此库对你有用,但愿在github上给我一个star哈。。。另外,欢迎fork,指望你们与我一块儿完善。后续会陆续的形式开源一些平时工做中积累的、Android实用的库,但愿你们会喜欢。。。
另外,建立了一个Android即时通信技术交流QQ群:1015178804,有须要的同窗能够加进来,不懂的问题,我会尽可能解答,一块儿学习,一块儿成长。
The end.