EventBus核心
Android和java的发布订阅事件总线,事件总是对发布订阅模式的实现,它是一种集中式的事件处理机制,允许不同组建之间进行彼此的通讯,而又不需要相互依赖,达到解耦的目的性。
优点
- 代码简单,快速。
- jar包小
- Activity和Fragment线程通讯优秀
- 稳定
fragment最初设计理念是适配和重用,并不必写过多的逻辑代码,
EventBus实现步骤
- 创建线程模式
- 创建注解
- 封装方法类
- 储存方法,并通过反射进行调用
大致思路是两个Acticity相互通讯的时候,可以借助中间管理类来进行方法的储存和调用。
废话少说上代码
先创建一个EventBus类,其作用是 管理类,负责将某个Activity的方法添加到其中,其他的Activity可以在里面寻找方法并调用
package com.tydfd.eventbusdemo;
import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @Classname EventBus
* @Description TODO
* @Date 2019/7/24 13:24
* @Created by liudo
* @Author by liudo
*/
public class EventBus {
//管理类 负责将某个activity的方法添加到其中,其他的activity可以在里面寻找方法并调用
//第一步先创建一个单例
//volatile
//1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
//2)禁止进行指令重排序。
private static volatile EventBus instance;
//定义存放方法的容器
private Map<Object, List<SubscribleMethod>> cacheMap;
private Handler mHandler;
private ExecutorService mExecutorService;
private EventBus(){
cacheMap = new HashMap<>();
mHandler = new Handler(Looper.getMainLooper());
mExecutorService = Executors.newCachedThreadPool();
}
public static EventBus getDefault(){
if(instance == null){
//代表这个方法加锁,相当于不管哪一个线程(例如线程A),
// 运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他同步方法),
// 有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这个方法后再运行此线程A,没有的话,锁定调用者
synchronized (EventBus.class){
if(instance == null){
instance = new EventBus();
}
}
}
return instance;
}
public void register(Object object) {
//寻找所有的obj类,带有subscribe注解的方法
List<SubscribleMethod> list = cacheMap.get(object);
if(list == null){
list = findSubscribleMethods(object);
cacheMap.put(object,list);
}
}
/**
* 寻找obj中的Subscrible注解的方法,放入集合中
* @param obj
* @return
*/
private List<SubscribleMethod> findSubscribleMethods(Object obj){
List<SubscribleMethod> list = new ArrayList<>();
Class<?> clazz = obj.getClass();
// 循环去查找父类是否存在subscrible注解方法
while (clazz!=null){
String name = clazz.getName();
// 判断当前是否是系统类,如果是,就退出循环
if(name.startsWith("java.")||name.startsWith("javax.")||name.startsWith("android.")){
break;
}
//得到所有的方法,这个clazz在本项目中,暂时指代的是MainActivity
Method[] Methods = clazz.getDeclaredMethods();
for(Method method : Methods){
//通过注解找到我们需要注册的方法
Subscrible subscrible = method.getAnnotation(Subscrible.class);
if(subscrible==null){
continue;
}
// 获取方法中的参数,并判断
Class<?>[] types = method.getParameterTypes();
if(types.length != 1){
Log.e("错误", "参数只允许一个");
}
//获取线程模式
ThreadMode threadMode = subscrible.threadMode();
SubscribleMethod sbm = new SubscribleMethod(method,threadMode,types[0]);
list.add(sbm);
}
clazz = clazz.getSuperclass();//寻找父类的subscribe
}
return list;
}
public void post(final Object type) {
//直接循环cacheMap里的所有的方法
Set<Object> set = cacheMap.keySet();
Iterator<Object> iterator = set.iterator();
while (iterator.hasNext()){
final Object obj = iterator.next();
List<SubscribleMethod> list = cacheMap.get(obj);
for (final SubscribleMethod subscribleMethod : list){
// 简单的理解:两个列对比一下,看看是否一致 (不严谨的说法)
// a(subscribleMethod.getType())对象所对应的类信息,是b(type.getClass())对象所对应的类信息的父类或者父接口
if(subscribleMethod.getType().isAssignableFrom(type.getClass())){
switch (subscribleMethod.getThreadMode()){
case MAIN:
//主 -主
if(Looper.myLooper() == Looper.getMainLooper()){
invoke(subscribleMethod,obj,type);
}else {
//子-主
mHandler.post(new Runnable() {
@Override
public void run() {
invoke(subscribleMethod,obj,type);
}
});
}
break;
case BACKGROUND:
//主 - 子
if(Looper.myLooper() == Looper.getMainLooper()){
mExecutorService.execute(new Runnable() {
@Override
public void run() {
invoke(subscribleMethod,obj,type);
}
});
}else {
//子 - 子
invoke(subscribleMethod,obj,type);
}
break;
}
}
}
}
}
private void invoke(SubscribleMethod subscribleMethod, Object obj, Object type) {
Method method = subscribleMethod.getMethod();
try {
method.invoke(obj,type);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
EventBus牵扯到主线程到子线程的调用故用系统创建线程池。
注解类
package com.tydfd.eventbusdemo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Classname Subscrible
* @Description TODO
* @Date 2019/7/24 13:37
* @Created by liudo
* @Author by liudo
*/
@Target(ElementType.METHOD)//作用于方法中
@Retention(RetentionPolicy.RUNTIME)// 注解会在class字节码文件中存在,jvm在加载时可以通过反射的方式获取该注解的内容
public @interface Subscrible {
ThreadMode threadMode() default ThreadMode.MAIN;
}
EventBus对方法进行封装的时候有这么几个属性 方法本体,线程模式,方法中的参数
下面是对这三个参数进行封装对象
package com.tydfd.eventbusdemo;
import java.lang.reflect.Method;
/**
* @Classname SubscribleMethod
* @Description TODO
* @Date 2019/7/24 13:53
* @Created by liudo
* @Author by liudo
*/
public class SubscribleMethod {
//方法体本身
private Method mMethod;
//线程模式
private ThreadMode mThreadMode;
//回调方法中的参数类型
private Class<?> type;
public SubscribleMethod(Method method, ThreadMode threadMode, Class<?> type) {
mMethod = method;
mThreadMode = threadMode;
this.type = type;
}
public Method getMethod() {
return mMethod;
}
public void setMethod(Method method) {
mMethod = method;
}
public ThreadMode getThreadMode() {
return mThreadMode;
}
public void setThreadMode(ThreadMode threadMode) {
mThreadMode = threadMode;
}
public Class<?> getType() {
return type;
}
public void setType(Class<?> type) {
this.type = type;
}
}
关于为何要用线程模式,线程模式有两个一个是主线程,一个是子线程。在进行异步加载的时候需要在子线程中加载。故需要设计一个枚举类
package com.tydfd.eventbusdemo;
/**
* @Classname THreadMode
* @Description TODO
* @Date 2019/7/24 13:47
* @Created by liudo
* @Author by liudo
*/
public enum ThreadMode {
//主线程
MAIN,
//子线程
BACKGROUND;
}
在MainActivity中使用是
package com.tydfd.eventbusdemo;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(MainActivity.this,SecondActivity.class));
}
});
}
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
//MainActivity将a方法放到eventBus
//不是所有的mainActivity的方法放进去
//通过注解的方法将该方法放进去
//注解就是给方法加上一个标记
@Subscrible(threadMode = ThreadMode.MAIN )
public void getEnent(EventBean eb){
Log.e("LIUDONG", "getEnent: "+Thread.currentThread().getName());
Log.e("LIUDONG",eb.toString()+"<=============================");
}
//Method 线程模式 方法中的参数
/* @Subscrible(threadMode = ThreadMode.MAIN)
public void b(User user){
}*/
}
在SecondActivity中实现是
package com.tydfd.eventbusdemo;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
findViewById(R.id.second).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//先去网络加载数据,异步数据加载
new Thread(){
@Override
public void run() {
super.run();
EventBus.getDefault().post(new EventBean("刘小董","目标进猪场养猪"));
Log.e("====> 2 ", "发送者 thread = " + Thread.currentThread().getName());
}
}.start();
}
});
}
}
代码是上面的所有代码,可能没有解绑的操作,具体查看EventBus源码大致思路跟本demo一样。本人也在学习中,特此记录一下技术点,好记性不如烂笔头。本人微信liudongGeek245210欢迎来交流。代码未完,后续会继续补充解绑删除。