EventBus
今天突然想起了EventBus,做Android的肯定或多或少知道点这东西,EventBus是一个非常强大的事件发布/订阅轻量级框架,我们可以通过他在Activity、Fragment之间进行通信,或者其他组件中,他的API也及其简单,使用也方便,能使得代码更简洁,那么既然EventBus很强大,今天就来手写一个具有基本功能的“EventBus”。
public class TestActivity extends AppCompatActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DataBindingUtil.setContentView(this, R.layout.activity_test);
EventBus.getDefault().register(this);
EventBus.getDefault().post("msg");
}
@Subscribe
public void onMsg(String msg){
Log.i(TAG, "onMsg: "+msg);
}
}
原理及实现
没有看过EventBus的源码,所以下面是按照自己思路来写,里面需要用到反射知识,在register的时候,获取这个类下所有带有Subscribe注解的方法,并把他放入集合中,当post发布事件的时候,获取参数类型,并且在现有方法集合查找,如果方法的参数和post时是一模一样的,那么通过invoke去调用,当然为了扩展,需要处理线程的逻辑。
在里面加入了延迟消息,可以在X秒后发布事件,这部分是通过DelayQueue队列来完成。
延迟消息信息:
public class DelayedMsg implements Delayed {
private Object[] param;
private long targetTimer;
public DelayedMsg( long targetTimer,Object ... param) {
this.param = param;
this.targetTimer = targetTimer;
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(targetTimer - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
return o.getDelay(TimeUnit.MILLISECONDS) - this.getDelay(TimeUnit.MILLISECONDS) > 0 ? -1 : 1;
}
public Object[] getParam() {
return param;
}
}
订阅注解,可以指明调用时的线程。
@Target({METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe {
ThreadMode thread() default ThreadMode.MAIN;
}
public enum ThreadMode {
MAIN,BACKGROUND;
}
EventBus实现:
package com.example.myapplication.event;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.example.myapplication.Subscribe;
import com.example.myapplication.ThreadMode;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
public class EventBus {
private int mThreadCount = 0;
private DelayQueue<DelayedMsg> mDelayedMsgQueue;
private Thread mDelayedMsgHandlerThread;
private Handler mHandler;
private ConcurrentHashMap<Object, List<Method>> mSubscribeMethodMap;
private volatile static EventBus mDefaultInstance;
private boolean mExitThread ;
public static EventBus getInstance() {
if (mDefaultInstance == null) {
synchronized (EventBus.class) {
if (mDefaultInstance == null) {
mDefaultInstance = new EventBus();
}
}
}
return mDefaultInstance;
}
public EventBus() {
mSubscribeMethodMap = new ConcurrentHashMap<>();
mHandler= new Handler(Looper.myLooper());
mDelayedMsgQueue=new DelayQueue<>();
}
private void startDelayedMsgHandlerThread(){
mExitThread=false;
mDelayedMsgHandlerThread =new Thread(new Runnable() {
@Override
public void run() {
while (!mExitThread){
try {
DelayedMsg take = mDelayedMsgQueue.poll(3, TimeUnit.SECONDS);
if (take!=null)
postMessage(take.getParam());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
mDelayedMsgHandlerThread.start();
}
/**
* 注册
*
* @param obj
*/
public void register(Object obj) {
if (mSubscribeMethodMap.size()==0){
startDelayedMsgHandlerThread();
}
Method[] declaredMethods = obj.getClass().getDeclaredMethods();
for (Method method : declaredMethods) {
if (method.getAnnotation(Subscribe.class) != null) {
setList(obj);
mSubscribeMethodMap.get(obj).add(method);
}
}
}
/**
* 删除
* @param obj
*/
public void unRegister(Object obj){
mSubscribeMethodMap.remove(obj);
if (mSubscribeMethodMap.size()==0){
mExitThread=true;
}
}
private void setList(Object obj) {
if (mSubscribeMethodMap.get(obj) == null) {
mSubscribeMethodMap.put(obj, new ArrayList<>());
}
}
public void postMessage(Object... msg) {
List<Class> msgParamClass = Arrays.stream(msg).map((Function<Object, Class>) o ->
o.getClass()).collect(Collectors.toList());
Iterator<Object> iterator = mSubscribeMethodMap.keySet().iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
List<Method> methods = mSubscribeMethodMap.get(next);
for (Method method : methods) {
if (equalsParameterType(method.getParameterTypes(), msgParamClass.toArray(new Class[msgParamClass.size()]))) {
method.setAccessible(true);
if (method.getAnnotation(Subscribe.class).thread() == ThreadMode.BACKGROUND) {
invokeMethodForBackground(next, method, msg);
continue;
}
invokeMethodForMain(next, method, msg);
}
}
}
}
public void postMessageDelayed(int delayedSecond , Object... msg) {
if (delayedSecond==0){
postMessage(msg);
return;
}
mDelayedMsgQueue.offer(new DelayedMsg(System.currentTimeMillis()+(1000*delayedSecond),msg));
}
private void invokeMethodForMain(Object obj, Method method, Object... param) {
try {
if (Thread.currentThread().getName().equals("main")){
method.invoke(obj, param);
return;
}
mHandler.post(() -> invokeMethodForMain(obj, method, param));
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
private void invokeMethodForBackground(Object obj, Method method, Object... param) {
mThreadCount++;
new Thread(() -> {
try {
method.invoke(obj, param);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
mThreadCount--;
}, getThreadName()).start();
}
private String getThreadName() {
return "eventbus-" + mThreadCount;
}
private boolean equalsParameterType(Class<?>[] clz1, Class<?>[] clz2) {
if (clz1.length != clz2.length) {
return false;
}
for (int i = 0; i < clz1.length; i++) {
if (!clz1[i].getName().equals(clz2[i].getName())) {
return false;
}
}
return true;
}
}
测试效果
package com.example.myapplication;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.example.myapplication.databinding.ActivityMainBinding;
import com.example.myapplication.event.EventBus;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding mActivityMainBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
EventBus.getInstance().register(this);
}
public void postInt(View view) {
EventBus.getInstance().postMessage(1);
}
public void postString(View view) {
EventBus.getInstance().postMessage("String Msg");
}
public void postObject(View view) {
EventBus.getInstance().postMessage(new Msg("1"));
}
public void postMultiParamMsg(View view){
EventBus.getInstance().postMessage(new Msg("听风逝夜"),"String msg");
}
public void postDelayMsg(View view){
EventBus.getInstance().postMessageDelayed(2,"听风逝夜");
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getInstance().unRegister(this);
}
@Subscribe(thread = ThreadMode.MAIN)
public void onPostMultiMsg(Msg msg ,String str) {
Log.i("TAG", "onPostDelayed: " + msg.getName()+ " "+" "+str + Thread.currentThread().getName());
}
@Subscribe(thread = ThreadMode.BACKGROUND)
public void onIntMsg(Integer msg) {
Log.i("TAG", "onIntMsg: " + msg + " " + Thread.currentThread().getName());
}
@Subscribe
public void onStringMsg(String msg) {
Log.i("TAG", "onStringMsg: " + msg);
}
@Subscribe
public void onMsg(Msg msg) {
Log.i("TAG", "onMsg: " + msg.getName());
}
static class Msg {
private String name;
public Msg(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
}
基本测试:
延迟&多参数测试:
当然还有很多细节没处理,比如类型判断,如果订阅时候是List类型,那么发布使用ArrayList则接收不到,另外基本类型需要使用包装类去接收。