Android手撕EventBus

EventBus核心

Android和java的发布订阅事件总线,事件总是对发布订阅模式的实现,它是一种集中式的事件处理机制,允许不同组建之间进行彼此的通讯,而又不需要相互依赖,达到解耦的目的性。

优点

  1. 代码简单,快速。
  2. jar包小
  3. Activity和Fragment线程通讯优秀
  4. 稳定

fragment最初设计理念是适配和重用,并不必写过多的逻辑代码,

EventBus实现步骤

  1. 创建线程模式
  2. 创建注解
  3. 封装方法类
  4. 储存方法,并通过反射进行调用

大致思路是两个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中使用是

扫描二维码关注公众号,回复: 8964193 查看本文章
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欢迎来交流。代码未完,后续会继续补充解绑删除。

发布了22 篇原创文章 · 获赞 4 · 访问量 4333

猜你喜欢

转载自blog.csdn.net/qq_38366111/article/details/97133630