Android-EventBus(手写简易版)

  • 简介
  • 基本使用
  • 手写核心地方
    • 注册 以及 取消注册
      • 流程图(EventBus注册的大致流程)
    • post发送消息
      • 流程图(EventBus发送消息的大致流程)
    • 使用我们自己简易的EventBus

简介

EventBus可以代替Android传统的Intent,Handler,Broadcast或接口函数,在Fragment,Activity,Service线程之间传递数据,执行方法
GitHub-EventBus

Subscribe :@Subscribe采用运行时注解,且注解只能用在函数上,默认的threadmode为posting

threadMode分为五种:
POSTING:默认的模式,开销最小的模式,因为声明为POSTING的订阅者会在发布的同一个线程调用,发布者在主线程那么订阅者也就在主线程,反之亦,避免了线程切换,如果不确定是否有耗时操作,谨慎使用,因为可能是在主线程发布;
MAIN:主线程调用,视发布线程不同处理不同,如果发布者在主线程那么直接调用(非阻塞式),如果发布者不在主线程那么阻塞式调用;
MAIN_ORDERED:和MAIN差不多,主线程调用,和MAIN不同的是他保证了post是非阻塞式的(默认走MAIN的非主线程的逻辑,所以可以做到非阻塞);
BACKGROUND:在子线程调用,如果发布在子线程那么直接在发布线程调用,如果发布在主线程那么将开启一个子线程来调用,这个子线程是阻塞式的,按顺序交付所有事件,所以也不适合做耗时任务,因为多个事件共用这一个后台线程;
ASYNC:在子线程调用,总是开启一个新的线程来调用,适用于做耗时任务,比如数据库操作,网络请求等,不适合做计算任务,会导致开启大量线程

基本使用

导入依赖

implementation 'org.greenrobot:eventbus:3.1.1'

接收消息的MainActivity界面

register(this) 注册绑定当前的Activity;
unregister(this) 解除绑定
@Subscribe(threadMode = ThreadMode.MAIN):指定在主线程中接收消息;

package com.lk.myeventbus;import androidx.appcompat.app.AppCompatActivity;import android.content.Intent;import android.os.Bundle;import android.view.View;import android.widget.TextView;import android.widget.Toast;import org.greenrobot.eventbus.EventBus;import org.greenrobot.eventbus.Subscribe;import org.greenrobot.eventbus.ThreadMode;public class MainActivity extends AppCompatActivity {    TextView tv;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);//注册绑定        EventBus.getDefault().register(this);        tv = findViewById(R.id.tv);    }//主线程中接收消息    @Subscribe(threadMode = ThreadMode.MAIN)    public void receive(Friend friend){        tv.setText(friend.toString());    }    @Override    protected void onDestroy() {        super.onDestroy();        //解除绑定        EventBus.getDefault().unregister(this);    }    public void change(View view) {    //这里我们跳转到另外一个Activity当中去发消息        startActivity(new Intent(MainActivity.this,TestActivity.class));    }}

发送消息的TestActivity界面

通过post发送一条消息,消息内容是个Friend对象

package com.lk.myeventbus;import android.app.Activity;import android.os.Bundle;import android.view.View;import org.greenrobot.eventbus.EventBus;import androidx.annotation.Nullable;public class TestActivity extends Activity {    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_test);    }    public void send(View view) {        new Thread(new Runnable() {            @Override            public void run() {            //我们在子线程中发个消息,看看接收消息界面在注解中指定主线程是否能成功                EventBus.getDefault().post(new Friend("lk",18));            }        }).start();    }}

手写核心地方

我们这里写一个简易版的EventBus,写出核心的地方

注册 以及 取消注册

根据EventBus的源码,我们这类单例;
在构造方法中,我们创建出来缓存订阅者以及对应的实体类;
至于这个Handler和线程池,是会用来发送消息的时候,注册中可以忽略;
这类我们定义一个注册和取消注册的方法;
注册的方法中:
1、先从缓存中去获取这个订阅者里面的所有标有注解的方法;
2、如果在缓存中没有获取到的话,我们就去并且添加到缓存的Map当中去;
怎么去获取所有标有注解的方法?
来看getSubscrbleMethods这个方法;
1、创建出一个集合来存储SubscrbleMethod这个实体类,也就是所有标有注解的方法存入到这个集合当中;
2、我们得一层一层的去寻找吧?因为当前的Activity可能会是继承了自己写的基类的BaseActivity吧?
3、然后我直接过滤掉系统的类;
4、获取到订阅者这个类里面的所有方法进行遍历;
5、在每个方法上面去寻找是否标有LkSubsrcible这个注解;
6、如果标有这个注解,我们就获取这个方法的所有参数;因为EventBus中,接收方法只能是一个参数,所以我们这里过滤掉不是一个参数的方法;
7、获取注解中的线程标识;
8、然后创建SubscrbleMethod实体类,存放方法体、方法执行的线程标识、参数类型;
添加到集合当中,并返回出去;
到这里我们就做完了注册订阅者

public class LkEventBus {    private volatile static LkEventBus instance;    private Handler handler;    //线程池    private ExecutorService executorService;    //缓存 订阅者对应所有标有注解的方法    private Map<Object, List<SubscrbleMethod>> cacheMap;    private LkEventBus(){        cacheMap = new HashMap<>();        //创建主线程的Handler        handler = new Handler(Looper.getMainLooper());        //初始化线程池        executorService = Executors.newCachedThreadPool();    }    /**     * 注册     * 保存订阅者信息     * @param subscriber 订阅者     */    public void register(Object subscriber){        //拿到订阅者类信息        Class<?> aClass = subscriber.getClass();        //先从缓存中获取 方法集合        List<SubscrbleMethod> subscrbleMethods = cacheMap.get(subscriber);        //如果缓存中没有获取到  也就是如果已经注册,就不需要注册添加到Map当中了        if(subscrbleMethods == null){            //去给我们创建            subscrbleMethods = getSubscrbleMethods(aClass);            //将订阅者和里面标有注解的所有方法存入缓存map中            cacheMap.put(subscriber,subscrbleMethods);        }    }    /**     * 获取订阅者的所有标有注解的方法     * @param subscriber 订阅者     * @return     */    private List<SubscrbleMethod> getSubscrbleMethods(Class<?> clazz) {        //创建一个List集合来保存所有的方法        List<SubscrbleMethod> list = new ArrayList<>();        //因为  订阅者  上一层  BaseActivity  上一层是Activity        while (clazz!=null){            //判断父类是在哪个包下(如果是系统的就不需要)            String name = clazz.getName();            //这些包下面的就系统的,我们直接跳过            if(name.startsWith("java.")                    || name.startsWith("javax.")                    || name.startsWith("android.")                    || name.startsWith("androidx.")){                break;            }            Method[] declaredMethods = clazz.getDeclaredMethods();            for (Method method : declaredMethods) {                //获取LkSubsrcible注解                LkSubsrcible annotation = method.getAnnotation(LkSubsrcible.class);                //没有标有这个注解                if(annotation==null){                    continue;                }                //如果标有这个注解                //获取这个方法的参数类型                Class<?>[] parameterTypes = method.getParameterTypes();                //因为EventBus里面,接收消息的方法中参数只能是一个                if(parameterTypes.length!=1){                    throw new RuntimeException(method.getName() +"方法只能接收一个参数");                }                //如果标有LkSubsrcible注解,并且参数个数也只有一个                //获取到这个方法上LkSubsrcible注解中的threadMode参数值                LkThreadMode lkThreadMode = annotation.threadMode();                //通过实体类保存  方法体、方法执行的线程标识、参数类型                SubscrbleMethod subscrbleMethod = new SubscrbleMethod(method, lkThreadMode, parameterTypes[0]);                //添加到集合当中                list.add(subscrbleMethod);            }            clazz = clazz.getSuperclass();        }        return list;    }      /**     * 取消注册     */    public void unregister(Object subscriber){        Class<?> aClass = subscriber.getClass();        List<SubscrbleMethod> list = cacheMap.get(subscriber);        if(list!=null){            //从缓存中去移除掉这个订阅信息            cacheMap.remove(subscriber);        }    }}

SubscrbleMethod 类

这个类用来存放订阅者的方法体、线程的标识、方法的参数类型

package com.lk.myeventbus.core.entity;import java.lang.reflect.Method;public class SubscrbleMethod {    //接收消息的方法体    private Method method;    //方法需要执行的线程    private LkThreadMode threadMode;    //方法的参数类型    private Class<?> parameteType;    public SubscrbleMethod(Method method, LkThreadMode threadMode, Class<?> parameteType) {        this.method = method;        this.threadMode = threadMode;        this.parameteType = parameteType;    }    public Method getMethod() {        return method;    }    public void setMethod(Method method) {        this.method = method;    }    public LkThreadMode getThreadMode() {        return threadMode;    }    public void setThreadMode(LkThreadMode threadMode) {        this.threadMode = threadMode;    }    public Class<?> getParameteType() {        return parameteType;    }    public void setParameteType(Class<?> parameteType) {        this.parameteType = parameteType;    }}

LkThreadMode 类

这个类我们和EventBus里面保持一致,用来做接收方法的线程标识

package com.lk.myeventbus.core.entity;/** * 标注方法需要执行的线程 * 我们把EventBus的线程标识类型拿过来 */public enum LkThreadMode {    POSTING,    MAIN,    MAIN_ORDERED,    BACKGROUND,    ASYNC}

流程图(EventBus注册的大致流程)


post发送消息

需要注意的是EventBus中会通过ThreadLoad去处理线程同步的问题,我们这类做了这一步的省略…
发送消息这个方法:
大致思路:我们需要去遍历订阅者集合,找到对应的方法,然后去对应的线程中执行该方法;
1、遍历缓存所有订阅者以及他们的所有标有LkSubsrcible注解的方法;
2、通过方法的参数类型进行比对发送的消息的数据类型;
3、如果找到符合要求的方法,再去获取到该方法注解里面的线程标识,做线程切换
4、然后在对应线程中去执行接收消息的方法,把参数传进方法中;

package com.lk.myeventbus.core;import android.os.Handler;import android.os.Looper;import com.lk.myeventbus.core.entity.LkThreadMode;import com.lk.myeventbus.core.entity.SubscrbleMethod;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;public class LkEventBus {    private volatile static LkEventBus instance;    private Handler handler;    //线程池    private ExecutorService executorService;    //缓存 订阅者对应所有标有注解的方法    private Map<Object, List<SubscrbleMethod>> cacheMap;    public static LkEventBus getDefault(){        if (instance == null) {            synchronized (LkEventBus.class) {                if (instance == null) {                    instance = new LkEventBus();                }            }        }        return instance;    }    private LkEventBus(){        cacheMap = new HashMap<>();        //创建主线程的Handler        handler = new Handler(Looper.getMainLooper());        //初始化线程池        executorService = Executors.newCachedThreadPool();    }    /**     * 发送消息     * @param obj     */    public void post(final Object obj){        //第一步线程同步这一块我们省略掉了        //第二步  遍历        //获取到key集合        Set<Object> set = cacheMap.keySet();        //获取迭代器       Iterator<Object> iterator = set.iterator();       //遍历       while (iterator.hasNext()){           //获取到订阅者  key是订阅者           final Object next = iterator.next();           //获取类中所有添加注解的方法           List<SubscrbleMethod> subscrbleMethods = cacheMap.get(next);           //遍历所有的方法           for (final SubscrbleMethod subscrbleMethod : subscrbleMethods) {               //判断这个方法是否是需要  根据参数类型去做判断               //isAssignableFrom 调用者和参数都是Class对象,调用者为父类,参数为本身或者其子类               //如果符合这个参数类型,也就是是需要接收消息的方法               if(subscrbleMethod.getParameteType().isAssignableFrom(obj.getClass())){                   //就去执行这个方法                   //下面做一下线程切换  通过注解中的线程标识去做                   switch (subscrbleMethod.getThreadMode()){                       case MAIN:   //接收方法是主线程标识                            //如果接收方法需要在主线程执行的 以及发送消息也是在主线程中发送的                           if(Looper.myLooper() == Looper.getMainLooper()){                               invoke(subscrbleMethod,next,obj);                           }else{   //如果接收如果接收方法需要在主线程执行的 而发送消息是在子线程中发送的                                //这里我们就通过handler去执行这个方法 发送消息                               handler.post(new Runnable() {                                   @Override                                   public void run() {                                       invoke(subscrbleMethod,next,obj);                                   }                               });                           }                           break;                       case ASYNC:  //接收方法是子线程标识                           //如果接收消息的方法是需要在子线程中  而当前发送消息的线程是在主线程中                           if(Looper.myLooper() == Looper.getMainLooper()){                               //这类就用线程池来执行                               executorService.execute(new Runnable() {                                   @Override                                   public void run() {                                       invoke(subscrbleMethod,next,obj);                                   }                               });                           }else{                               //如果接收消息的方法是需要在子线程中  并且当前发送消息的线程也是在子线程                               //直接执行                               invoke(subscrbleMethod,next,obj);                           }                           break;                       case POSTING:    //接收方法是默认线程标识                           //默认的情况,我们就不管线程了                           invoke(subscrbleMethod,next,obj);                           break;                   }               }           }       }    }    /**     * 执行对应的方法     * @param subscrbleMethod     * @param subscriber  订阅者实例     * @param obj   方法执行的参数     */    private void invoke(SubscrbleMethod subscrbleMethod, Object subscriber, Object obj) {        try {            subscrbleMethod.getMethod().invoke(subscriber,obj);        } catch (Exception e) {            e.printStackTrace();        }    }}

流程图(EventBus发送消息的大致流程)


使用我们自己简易的EventBus

注册、取消注册、接收消息

package com.lk.myeventbus;import androidx.appcompat.app.AppCompatActivity;import android.content.Intent;import android.os.Bundle;import android.view.View;import android.widget.TextView;import android.widget.Toast;import com.lk.myeventbus.core.LkEventBus;import com.lk.myeventbus.core.LkSubsrcible;import com.lk.myeventbus.core.entity.LkThreadMode;import org.greenrobot.eventbus.EventBus;import org.greenrobot.eventbus.Subscribe;import org.greenrobot.eventbus.ThreadMode;public class MainActivity extends AppCompatActivity {    TextView tv;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //这里是EventBus的注册//        EventBus.getDefault().register(this);        //我们来使用自己的EventBus进行注册        LkEventBus.getDefault().register(this);        tv = findViewById(R.id.tv);    }    //EventBus的注解标识//    @Subscribe(threadMode = ThreadMode.MAIN)    //我们换上自己的    @LkSubsrcible(threadMode = LkThreadMode.MAIN)    public void receive(Friend friend){        tv.setText(friend.toString());    }    @Override    protected void onDestroy() {        super.onDestroy();        //EventBus的取消注册//        EventBus.getDefault().unregister(this);        //用我们自己的        LkEventBus.getDefault().unregister(this);    }    public void change(View view) {        startActivity(new Intent(MainActivity.this,TestActivity.class));    }}

发送消息

package com.lk.myeventbus;import android.app.Activity;import android.os.Bundle;import android.view.View;import com.lk.myeventbus.core.LkEventBus;import org.greenrobot.eventbus.EventBus;import androidx.annotation.Nullable;public class TestActivity extends Activity {    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_test);    }    public void send(View view) {        new Thread(new Runnable() {            @Override            public void run() {//                EventBus.getDefault().post(new Friend("lk",18));                LkEventBus.getDefault().post(new Friend("lk",18));            }        }).start();    }}

相互学习和进步,如有错误请指出,大神勿喷,谢谢

更多相关文章

  1. AndroidStudio中的图片资源存放位置以及drawable文件夹的创建方
  2. Android四大组件生命周期,组件类的继承与实现,Context
  3. Java 四种线程池的用法分析
  4. android,ExpandableList,将被选中的group置于顶端
  5. Android(安卓)挂断电话流程
  6. 深入源码分析Handler的消息处理机制
  7. Android中的自定义注解
  8. 查看Linux & Android中内存占用方法
  9. android 起动APP时锁住(Lock apps)

随机推荐

  1. Android 网络框架学习之Retrofit
  2. 红茶一杯话Binder(传输机制篇_下)
  3. Linux(Android):如何禁用Intel DPST(显示节电
  4. 【原创】安卓程序员的大革命,Cocovr框架库
  5. Android开发中MinSDK与TargetSDK不在同一
  6. android项目 之 记事本(14) ----- 手势缩放
  7. 使用RecyclerView的AppBarLayout可以在不
  8. 更新后-崩溃com.google.android.gms:play
  9. Android 进阶:Fragment 源码深入理解
  10. 显示操作栏和向上导航 - Android