一直觉的写个eventbus框架应该很难, 今天自己通过一些资料学习了一下, 发现实现一个简易的也不难.
先说EventBus的用法, 在一个界面注册后, 其他界面发消息, 这个注册的界面就可以收到消息并处理.
下面来一步步的实现一个Eventbus吧~
一. 看看简单用法;
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//先注册
EventBus.getInstance().register(this)
}
// 通过注解来接受消息. 可以指定线程,可以执行该方法.
@Subscribe(threadMode = ThreadMode.MAIN)
fun getMessage(s: String) {
Log.d("收货线程", "" + Thread.currentThread().name)
Toast.makeText(this, s, Toast.LENGTH_SHORT).show()
}
override fun onDestroy() {
super.onDestroy()
EventBus.getInstance().unRegister(this)
}
}
二. Evevtbus原理解析;
- 1.首先是注册过程. 注册要把当前类的class文件传入.
- 2.有了class文件, 可以找到那些方法被指定注解标记过.
- 3.把这个方法(参数类型, 线程类型, 方法体) 保存在一个容器中.
- 4.其他界面发送消息就去遍历这个容器, 找到容器中参数类型匹配的方法.
- 5.在指定的线程中执行这个方法.
三. 手写Evevtbus框架;
一. 写一个Evevtbus 单例. 有注册, 注销方法.
public class EventBus {
private EventBus() {
}
private static EventBus instance;
public static EventBus getInstance() {
if (instance == null) {
instance = new EventBus();
}
return instance;
}
// 注册
public void register(Object object) {
}
// 注销
public void unRegister(Object object) {
}
}
二. 定义线程的枚举类,
public enum ThreadMode {
/**
* 在主线程中回调事件
*/
MAIN,
/**
* 在子线程中回调事件
*/
BACKGROUND,
/**
* 在默认线程中回调事件
*/
POSTING
}
三. 自定义注解Subscribe,
@Target(ElementType.METHOD) //注解标注在方法上
@Retention(RetentionPolicy.RUNTIME) // 注解在运行的时候起作用 .java --> .class --> .dex
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
}
四. 封装方法体
容器中要装入注解标注的方法. 所以封装方法体. 如下;
public class MethodManager {
/** 方法所在的线程 */
ThreadMode threadMode;
/** 方法 */
Method method;
/** 形式参数 */
Class<?> type;
public MethodManager(ThreadMode threadMode, Method method, Class<?> type) {
this.threadMode = threadMode;
this.method = method;
this.type = type;
}
public ThreadMode getThreadMode() {
return threadMode;
}
public void setThreadMode(ThreadMode threadMode) {
this.threadMode = threadMode;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public Class<?> getType() {
return type;
}
public void setType(Class<Object> type) {
this.type = type;
}
}
五. 注册过程和发消息
一个界面注册过程j就是要通过注解找到标记的方法, 存在一个集合中, 因为有很多界面要注册, 所以用map容器存. key对应的界面, value 对应这个界面标记方法的集合. 发消息其实就是对这个集合遍历, 找到合适的方法. 然后去执行
public class EventBus {
/**
* 注册后, 其实是类中注解标记的方法 放入集合中
*/
Map<Object, List<MethodManager>> map;
ExecutorService executorService;
Handler mhander = new Handler(Looper.getMainLooper()); //获取主线程hander
private EventBus() {
map = new HashMap<>();
executorService = Executors.newCachedThreadPool();
}
private static EventBus instance;
public static EventBus getInstance() {
if (instance == null) {
instance = new EventBus();
}
return instance;
}
/**
* 注册
*
* @param object
*/
public void register(Object object) {
List<MethodManager> methodManagers = map.get(object);
if (methodManagers == null) {
findObject(object);
}
}
private void findObject(Object object) {
Class<?> aClass = object.getClass();
Method[] declaredMethods = aClass.getDeclaredMethods();
List<MethodManager> list = new ArrayList<>();
for (Method declaredMethod : declaredMethods) {
Subscribe annotation = declaredMethod.getAnnotation(Subscribe.class);
if (annotation == null) {
continue;
}
ThreadMode threadMode = annotation.threadMode(); //线程标记
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
if (parameterTypes == null || parameterTypes.length > 1) {
continue;
}
Class<?> parameterType = parameterTypes[0];
MethodManager methodManager = new MethodManager(threadMode, declaredMethod, parameterType);
list.add(methodManager);
}
map.put(object, list);
}
// 发送消息其实就是对集合进行遍历
public void post(final Object object) {
Set<Object> objects = map.keySet();
Iterator<Object> iterator = objects.iterator();
while (iterator.hasNext()) {
final Object next = iterator.next();
List<MethodManager> list = map.get(next);
for (final MethodManager methodManager : list) {
//比较发送端的消息类型和接受者的消息类型是否一致
if (methodManager.getType().isAssignableFrom(object.getClass())) {
ThreadMode threadMode = methodManager.getThreadMode();
invok(next, object, methodManager.getMethod());
}
}
}
}
/**
* 找到匹配的方法了, 就去执行改方法
*
* @param next
* @param object
* @param method
*/
private void invok(Object next, Object object, Method method) {
try {
method.invoke(next, object);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public void unRegister(Object object) {
if (map.get(object) != null) {
map.remove(object);
}
}
}
六. 线程切换过程
线程切换,其实就是执行方法的时候, 判断需要在哪个线程执行, 去切换一下就好了. 切换线程如下:
if (methodManager.getType().isAssignableFrom(object.getClass())) {
ThreadMode threadMode = methodManager.getThreadMode();
if (threadMode == ThreadMode.MAIN) { //在主线程中执行方法
if (Looper.myLooper() == Looper.getMainLooper()) {
invok(next, object, methodManager.getMethod());
} else {
mhander.post(new Runnable() {
@Override
public void run() {
invok(next, object, methodManager.getMethod());
}
});
}
} else if (threadMode == ThreadMode.BACKGROUND) { //在子线程中执行方法
if (Looper.myLooper() == Looper.getMainLooper()) {
executorService.execute(new Runnable() {
@Override
public void run() {
invok(next, object, methodManager.getMethod());
}
});
} else {
invok(next, object, methodManager.getMethod());
}
} else if (threadMode == ThreadMode.POSTING) {
invok(next, object, methodManager.getMethod());
}
}
七. 完成.
写到这里就完成这个简单的Eventbus框架 . 其实原理和实现其实都不复杂. 其实还有个问题就是在多进程中, 这个手写的eventbus其实是无效的, 因为单例不能夸进程. 如果想夸进程发消息, 下一次再研究.