Welcome to reprint, reprint please indicate the source: juejin.cn/post/684490…

Writing in the front

Android IM library based on Netty+TCP+Protobuf The following article is a self-defined Android event distribution center library, which implements event publish-subscribe functions like system broadcast, EventBus and RxBus. If there is time later, Will continue to improve the previous NettyChat library, including adding WebSocket protocol implementation, UDP protocol implementation and UI page encapsulation (including session list page, message list page, etc.), please look forward to.

This article CEventCenter callback implementations based on object pool and interface, mainly to solve in the Activity/fragments/Service between news events, due to the limited level of the author, in this paper, we have not welcome criticism and corrections.

As always, here’s how it works:You can see that the TextView text in an Activity changes after B Activity publishes the event. Github fork Github fork Github forkMaking the address

Next, let’s get down to business.


Why not use BroadcastReceiver?

First, BroadcastReceiver is a heavyweight, resource-intensive approach. Second, as we all know, the onReceive() method runs on the main thread and cannot take more than 10 seconds to execute, otherwise it will cause ANR. In onReceive(), you can start a child thread to handle time-consuming tasks. This approach, not to say no, just not reliable. Receiver is active only when the onReceive method is executed. Once onReceive returns, Receiver is no longer active. Since an activity can be stopped by the user, the BroadcastReceiver itself has a very short life cycle. It is possible for the activity to be stopped by the user before the child thread ends, or for the BroadcastReceiver to end. When the Activity has stopped and the BroadcastReceiver has ended, the process they are in becomes an empty process (a process with no active components) and may be killed first when the system needs memory. If the host process is terminated, all child threads within that process are also terminated, which may cause the child threads to fail to complete. Why can’t you start child threads in BroadcastReceiver


Why not use RxBus?

In fact, I also use it. I remember that in early 2017, we were working on a live broadcast project, in which we used RxBus to transmit news and events. At that time, it was like this: Audience to host a gift is the im sending a message to the service end, server received gifts, gifts, the logic of processing, then to the client return gift status messages, after the client receives the message, by RxBus messaging to Activity (actually here via im or HTTP interface request, have the same problem). During the pressure test, a gift is sent every 50ms, and there is a high probability of a bug, which is the one shown below:This bug, because we had to use RxBus version, is to use RxJava1.0 within, and RxJava1.0 is a design flaw, that is not support the back pressure, in a nutshell, throw MissingBackpressureException often is because, The observed sends the event too fast, and the observer processes it too slowly, and you haven’t done anything about it, so report an exception.

At that time, MY mind was blown, because the project was huge, if I abandoned RxBus, the workload would be huge, and the project was in a hurry to launch, so I had no choice but to replace all the places where the bug might occur with the CEventCenter implemented by myself, and then gradually replace it. Of course, the current RxJava has fixed the problem of back pressure, and CEventCenter has been in use since then. So far, no problems have been found, so it is still in use. If you use this library, please contact me if you find any problems during the use. If you already have a good library, there’s no need to reinvent it. Of course, if time permits, it’s good to write your own library, at least in the process, you’ll learn something.

Why not use EventBus?

The latest version of EventBus is supposed to be 3.x, which has a somewhat awkward design: Events can only be distinguished by their class name. This raises at least three big questions:

  1. You have to define a class for every event;
  2. Increase the number of methods;
  3. Causes both event senders and receivers to rely on coupled event classes.

EventBus’s thread model and support for sticky events are excellent.


What is an object pool? Why use object pools?

As mentioned above, CEventCenter is based on object pooling and interface callbacks. What is object pooling? In fact, if you’ve used OkHttp, if you’ve read the source code, there’s something called connection pooling in the OkHttp source code, and object pooling is similar to connection pooling. In Java, the object life cycle includes object creation, object use, object disappear three time periods, the object use is the object really need to survive time, it is not easy to modify, should be used when still have to use ah. Object creation and disappearance need to be controlled. The creation of an object is time-consuming, and may not feel like it, just as an assignment of int I =1 is time-consuming, and constructing an object, an array, for example, is even more time-consuming. Besides, the elimination of objects, in Java using GC to recycle objects, in fact, also need to monitor every running state of objects, including reference, assignment and so on. During Full GC, other operations are paused and the CPU is monopolized. Therefore, we need to control the number of objects created, and do not easily let the object disappear, so that its reuse more fully. Excerpt from: Java object pooling technology

Without further ado, let’s just get started.

First, define an object interface to be used in the object pool: pooledobject.java

package com.freddy.event; /** * <p>@ProjectName: CEventCenter</p> * <p>@ClassName: PooledObject.java</p> * <p>@PackageName: Com.freddy. Event </p> * <b> * <p> @description: Objects in the object pool require the implementation of the PooledObject interface </p> * </b> * <p>@author: FreddyChen</p> * <p>@date: 2019/04/25 16:59</p> * <p>@email: [email protected]</p> */ public interface PooledObject {/** * restore the default state */ void reset(); }Copy the code

Then, define an event model, which is the message event object that needs to be delivered: cEvent.java

package com.freddy.event; /** * <p>@ProjectName: CEventCenter</p> * <p>@ClassName: CEvent.java</p> * <p>@PackageName: Com. Freddy. Event < / p > < p > < b > * * @ Description: event model < / b > < / p > * * < p > @ author: FreddyChen * < / p > < p > @ the date: 2019/04/25 17:24</p> * <p>@email: [email protected]</p> */ public class CEvent implements PooledObject { private String topic; Private int msgCode; // Message type 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; }}Copy the code

Next, define a custom ObjectPool: objectpool.java

package com.freddy.event; /** * <p>@ProjectName: CEventCenter</p> * <p>@ClassName: ObjectPool.java</p> * <p>@PackageName: Com. Freddy. Event < / p > < p > < b > * * @ Description: custom object pooling < / b > < / p > * * < p > @ author: FreddyChen * < / p > < p > @ the date: 2019/04/25 17:30</p> * <p>@email: [email protected]</p> */ public abstract class ObjectPool<T extends PooledObject> { private T[] mContainer; Private Final Object LOCK = new Object(); Private int length; Public ObjectPool(int capacity) {mContainer = createObjPool(capacity); mContainer = createObjPool(capacity); } /** * create object pool ** @param capacity maximum capacity * @return object pool */ protected abstract T[] createObjPool(int capacity); /** * create a new object ** @return */ protected createNewObj(); ** @return */ public final T get() {// Find a free object from the pool, if there is none, T obj = findFreeObject(); if (null == obj) { obj = createNewObj(); } else {// Clear object state obj.reset(); } return obj; Private T findFreeObject() {T obj = null; synchronized (LOCK) { if (length > 0) { --length; obj = mContainer[length]; MContainer [length] = null; } } return obj; Public final void returnObj(T obj) {synchronized (LOCK) {int size = mContainer.length; if (length < size) { mContainer[length] = obj; length++; }}}}Copy the code

Then, you define a custom event object pool that inherits from the custom object pool: ceventPool.java

package com.freddy.event; /** * <p>@ProjectName: CEventCenter</p> * <p>@ClassName: CEventObjPool.java</p> * <p>@PackageName: Com. Freddy. Event < / p > < p > < b > * * @ the Description: the event object pool < / b > < / p > * * < p > @ author: FreddyChen * < / p > < p > @ the date: 2019/04/25 17:45</p> * <p>@email: [email protected]</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(); }}Copy the code

There is also an event listener: i_cEventListener.java

package com.freddy.event; /** * <p>@ProjectName: CEventCenter</p> * <p>@ClassName: I_CEventListener.java</p> * <p>@PackageName: Com. Freddy. Event < / p > < p > < b > * * @ Description: event listener < / b > < / p > * * < p > @ author: FreddyChen * < / p > < p > @ the date: 2019/04/25 17:52</p> * <p>@email: [email protected]</p> */ public interface I_CEventListener {/** ** Event callback * <b> note: </b><br /> * If obj uses the object pool, <br /> * Then when the event is completed, obj is automatically recycled to the object pool, please do not continue to use other threads. * @param topic * @param msgCode * @param resultCode * @param obj */ void onCEvent(String topic, int msgCode, int resultCode, Object obj); }Copy the code

Finally, there is our main character, the event distribution center: 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 > < p > < b > * * @ Description: class Description < / b > < / p > * * < p > @ author: FreddyChen * < / p > < p > @ the date: 2019/04/25 17:48</p> * <p>@email: [email protected]</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 topic single topic */ public static void ** @param topic single topic */ public static void ** @param topic single topic */ onBindEvent(boolean toBind, I_CEventListener listener, String topic) { onBindEvent(toBind, listener, new String[]{topic}); } /** * register/unregister listener ** @param toBind true register false unregister listener * @param Topics multiple topics */ public static void onBindEvent(boolean toBind, I_CEventListener listener, String[] topics) { if (toBind) { registerEventListener(listener, topics); } else { unregisterEventListener(listener, topics); } /** * register listener ** @param Listener * @param topic single topic */ public static void registerEventListener(I_CEventListener)  listener, String topic) { registerEventListener(listener, new String[]{topic}); } /** * register listener ** @param Listener * @param Topics multiple 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); Listener_map.put (topic, listener); listener_map.put (topic, listener); 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 (listeners <I_CEventListener> listeners = (listeners <I_CEventListener> listeners);  If (listeners. IndexOf (listener) >= 0) {// continue; } listeners.add(listener); }}}} /** * logout listener ** @param Listener * @param topic single topic */ public static void unregisterEventListener(I_CEventListener listener, String topic) { unregisterEventListener(listener, new String[]{topic}); } /** * log out listener ** @param Listener * @param Topics multiple 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); * * * * * * * * * * * * * * * * * * * * * * * obj; listeners.remove(listener); }}}} /** * synchronous distribution events ** @param Topic topic * @param msgCode Message type * @param resultCode Reserved parameter * @param obj Callback returns data */ 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); }} public static void dispatchEvent(CEvent 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(); }} // Send event 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()); // Put the object back into the POOL. ReturnObj (event); }}}Copy the code

The code is relatively simple, is to register listener -> issue event -> event callback response -> logout listener four steps, support one to one, one to many issue topic events, events after distribution, put the object back to the object pool, easy object reuse. Take an example of an Activity:

  1. In the onCreate() method of an Activity, Call CEventCenter. RegisterEventListener (I_CEventListener listener, String topic/String [] switchable viewer) method to register the listener, OnCEvent (String Topic, int msgCode, int resultCode, Object obj) At the same time don’t forget in onDestroy () method call CEventCenter. UnregisterEventListener (I_CEventListener listener, The String topic/String[] topics) method unlogs the listener as shown below:

Next, in B Activity, callCEventCenter.dispatchEvent(CEvent event/ String topic, int msgCode, int resultCode, Object obj)Method to publish events, as shown below:And then you’ll find an ActivityonCEvent(String topic, int msgCode, int resultCode, Object obj)Will call back, in this method to perform the corresponding logic processing is ok.

Let’s see how it works:

You can see that the TextView text in an Activity changes after B Activity publishes the event. In addition, multiple event listeners register/unregister the same way, just replace String with String[].

Making the address

Write in the last

This article is relatively simple, because the previous article posted a large number of pictures, leading to the loading may be too slow, and it is not so convenient to see the code screenshots on the phone, so this article is mostly replaced by direct paste source code, convenient for everyone to read. The function of this library is relatively simple, so the source code can be posted, if this library is useful to you, I hope to give a star on Github ha… By the way, welcome to Fork and I look forward to working with you on it. The subsequent form of open source in succession some peacetime work accumulated, Android practical library, I hope you will like…

In addition, I created an Android INSTANT messaging technology communication QQ group: 1015178804, students who need to add, I will try to answer the questions that do not understand, learn together, grow together.

PS: The newly opened public account can not leave a message, if you have different opinions or suggestions, you can go to the nuggets comment or add to the QQ group: 1015178804, if the group is full, you can also give me a private message on the public account, thank you.

Post the official number:

FreddyChen

The end.