EventBus系列文章(二) - 手写EventBus

前言

EventBus系列文章
EventBus系列文章(一) - register()、subscribe()、post()分析
EventBus系列文章(二) - 手写EventBus

1. 手写EventBus

从MainActivity跳转TestActivity,然后点击TestActivity,把数据会传到MainActivity即可;

2. 代码如下
1>:MainActivity代码如下:
/**
 * Email: [email protected]
 * Created by Novate 2018/6/16 8:42
 * Version 1.0
 * Params:
 * Description:
*/
public class MainActivity extends AppCompatActivity {

    private TextView mTv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 注册,思考为什么要注册?
        EventBus.getDefault().register(this);

        // 进入测试界面
        mTv = (TextView) findViewById(R.id.test_tv);
        mTv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this,TestActivity.class);
                startActivity(intent);
            }
        });
    }

    /**
     * threadMode 执行的线程方式
     * priority 执行的优先级
     * sticky 粘性事件
     */
    @Subscribe(threadMode = ThreadMode.MAIN,priority = 50,sticky = true)
    public void test1(String msg){
        // 如果有一个地方用 EventBus 发送一个 String 对象,那么这个方法就会被执行
        Log.e("TAG","msg1 = "+msg);
        mTv.setText(msg);
    }

    /**
     * threadMode 执行的线程方式
     * priority 执行的优先级,值越大优先级越高
     * sticky 粘性事件
     */
    @Subscribe(threadMode = ThreadMode.MAIN,priority = 100,sticky = true)
    public void test2(String msg){
        // 如果有一个地方用 EventBus 发送一个 String 对象,那么这个方法就会被执行
        Log.e("TAG","msg2 = "+msg);
        mTv.setText(msg);
    }

    @Override
    protected void onDestroy() {
        // 解绑,思考为什么要解绑?
        EventBus.getDefault().unregister(this);
        super.onDestroy();
    }
}
2>:TestActivity代码如下:
/**
 * Email: [email protected]
 * Created by Novate 2018/6/16 8:42
 * Version 1.0
 * Params:
 * Description:
*/

public class TestActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.test_tv).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EventBus.getDefault().post("text");
            }
        });
    }
}
3>:EventBus代码如下
/**
 * Email: [email protected]
 * Created by Novate 2018/6/10 11:08
 * Version 1.0
 * Params:
 * Description:
*/

public class EventBus {

    // subscriptionsByEventType 这个集合存放的是?
    // key 是 Event 参数的类
    // value 存放的是 Subscription 的集合列表
    // Subscription 包含两个属性,一个是 subscriber 订阅者(反射执行对象),一个是 SubscriberMethod 注解方法的所有属性参数值
    private final Map, CopyOnWriteArrayList> subscriptionsByEventType;
    // typesBySubscriber 这个集合存放的是?
    // key 是所有的订阅者
    // value 是所有订阅者里面方法的参数的class
    private final Map>> typesBySubscriber;


    private EventBus(){
        typesBySubscriber = new HashMap>>() ;
        subscriptionsByEventType = new HashMap<>() ;
    }
    static volatile EventBus defaultInstance;

    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }


    /**
     *
     * @param object:就是MAinActivity.this
     */
    public void register(Object object) {
        List subscriberMethods = new ArrayList<>() ;
        // 1. 解析所有方法,封装成 SubscriberMethod的集合
        // a:获取class文件对象
        Class objClass = object.getClass();
        // b:获取MainActivty中所有的方法
        Method[] methods = objClass.getDeclaredMethods();
        // c:for循环
        for (Method method : methods) {
            // 通过 MAinActivity中的注解Subscribe来获取对应 Subscribe的方法,也就是test1()、test2()
            Subscribe subscribe = method.getAnnotation(Subscribe.class);
            if (subscribe != null){
                // 获取所有Subscribe属性,解析出来,这个表示test1()或者test2()方法中有几个参数,只能有1个参数,如果有多个,直接抛异常,
                // 这里就当成它只有1个参数,就直接取数组的第0个位置的元素即可
                Class[] parameterTypes = method.getParameterTypes();

                SubscriberMethod subscriberMethod = new SubscriberMethod(method ,  // test1() 和 test2()方法
                        parameterTypes[0] ,    // 只是取test1()或者 test2()方法中的 第一个参数
                        subscribe.threadMode() ,   // 线程模式 主线程、子线程
                        subscribe.priority() ,  // 优先级
                        subscribe.sticky()) ;  // 是否是粘性事件

                // 有一个符合条件的,就给集合中存储一个对象
                subscriberMethods.add(subscriberMethod) ;
            }
        }
        // 2. 按照规则,存放到 subscriptionsByEventType集合 里面去,这个是map集合
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            // 注册
            subscriber(object , subscriberMethod) ;
        }
    }


    /**
     * 注册
     * @param object:MainActivity.this
     * @param subscriberMethod:MainActivity中符合含有注解Subscriber的方法,也就是test1()和test2()方法
     */
    private void subscriber(Object object, SubscriberMethod subscriberMethod) {
        Class eventType = subscriberMethod.eventType;

        // 根据eventType键,获取对应的值
        CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null){
            // 线程安全的集合
            subscriptions = new CopyOnWriteArrayList<>() ;
            // 根据键值对存储数据到 map集合
            subscriptionsByEventType.put(eventType , subscriptions) ;
        }

        // 这里直接忽略判断优先级,这里直接添加
        Subscription subscription = new Subscription(object , subscriberMethod) ;
        // 把对象添加到集合中
        subscriptions.add(subscription) ;

        // typesBySubscriber要弄好,是为了方便移除
        List> eventTypes = typesBySubscriber.get(object);
        if (eventTypes == null){
            eventTypes = new ArrayList<>() ;
            typesBySubscriber.put(object , eventTypes) ;
        }

        if (!eventTypes.contains(eventType)){
            eventTypes.add(eventType) ;
        }
    }


    /**
     * 注销移除
     * @param object:MainActivity
     */
    public void unregister(Object object) {
        List> eventTypes = typesBySubscriber.get(object);
        if (eventTypes != null){
            for (Class eventType : eventTypes) {
                removeObject(eventType , object) ;
            }
        }
    }


    /**
     * 移除
     */
    private void removeObject(Class eventType, Object object) {
        // 一边for循环,一边移除是不行的
        // 获取事件类的所有订阅信息列表,将订阅信息从订阅信息集合中移除,同时将订阅信息中的active属性置为FALSE
        List subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions != null) {
            int size = subscriptions.size();
            for (int i = 0; i < size; i++) {
                Subscription subscription = subscriptions.get(i);
                if (subscription.subscriber == object) {
                    // 将订阅信息从集合中移除
                    subscriptions.remove(i);
                    i--;
                    size--;
                }
            }
        }
    }


    /**
     * TestActivity中需要发送数据的post()方法
     */
    public void post(Object event) {
        // 遍历subscriptionsByEventType的map集合,
        // 也就是遍历test1()和test()2这两个方法,
        // 找到符合的方法,然后调用方法的 method.invoke()执行
        // 要注意线程的切换

        // 获取class文件对象
        Class eventType = event.getClass();
        // 找到符合的方法,然后调用方法的 method.invoke()执行
        CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions != null){
            for (Subscription subscription : subscriptions) {
                executeMethod(subscription , eventType) ;
            }
        }
    }


    /**
     * 执行
     */
    private void executeMethod(final Subscription subscription, final Class event) {
        // 获取threadMode
        ThreadMode threadMode = subscription.subscriberMethod.threadMode;
        // 判断是否是主线程:一个线程只有一个 looper对象
        boolean isMainThread = Looper.getMainLooper() == Looper.myLooper();
        // 枚举
        switch (threadMode){
            // 如果发送的是在主线程,就在主线程,如果发送的是在子线程,就在子线程
            case POSTING:
                 invokeMethod(subscription , event) ;
                 break;
            // 主线程
            case MAIN:
                 if (isMainThread){
                     invokeMethod(subscription , event) ;
                 }else{
                     // 这里必须添加Looper.myLooper(),否则会报错,因为在主线程可以直接new Handler(),
                     // 在子线程如果new Handler(),就必须添加上 Looper.perpare()否则会报错
                     Handler handler = new Handler(Looper.myLooper()) ;
                     handler.post(new Runnable() {
                         @Override
                         public void run() {
                             invokeMethod(subscription , event) ;
                         }
                     }) ;
                 }
                 break;
            // 异步
            case ASYNC:
                AsyncPoster.enqueue(subscription , event);
                 break;
            case BACKGROUND:
                 if (!isMainThread){
                     invokeMethod(subscription , event) ;
                 }else{
                    AsyncPoster.enqueue(subscription , event);
                 }
                 break;
        }
    }



    private void invokeMethod(Subscription subscription , Class event) {
        try {
            subscription.subscriberMethod.method.invoke(subscription.subscriber ,   // 对象
                    event) ;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
4>:AsyncPoster代码如下:
/**
 * Posts events in background.
 * 
 * @author Markus
 */
class AsyncPoster implements Runnable {
    Subscription subscription;
    Object event;

    // 线程池
    private final static ExecutorService executorService = Executors.newCachedThreadPool();

    public AsyncPoster(Subscription subscription, Object event){
        this.subscription = subscription;
        this.event = event;
    }

    public static void enqueue(Subscription subscription, Object event) {
        AsyncPoster asyncPoster = new AsyncPoster(subscription,event);
        // 用线程池
        executorService.execute(asyncPoster);
    }

    @Override
    public void run() {
        try {
            subscription.subscriberMethod.method.invoke(subscription.subscriber,event);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
5>:Subscribe注解代码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;

    boolean sticky() default false;

    /** Subscriber priority to influence the order of event delivery.
     * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
     * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
     * delivery among subscribers with different {@link ThreadMode}s! */
    int priority() default 0;
}
>6:SubscriberMethod代码如下:
/** Used internally by EventBus and generated subscriber indexes. */
public class SubscriberMethod {
    final Method method;
    final ThreadMode threadMode;
    final Class eventType;
    final int priority;
    final boolean sticky;
    /** Used for efficient comparison */
    String methodString;

    public SubscriberMethod(Method method, Class eventType, ThreadMode threadMode, int priority, boolean sticky) {
        this.method = method;
        this.threadMode = threadMode;
        this.eventType = eventType;
        this.priority = priority;
        this.sticky = sticky;
    }

    @Override
    public boolean equals(Object other) {
        if (other == this) {
            return true;
        } else if (other instanceof SubscriberMethod) {
            checkMethodString();
            SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other;
            otherSubscriberMethod.checkMethodString();
            // Don't use method.equals because of http://code.google.com/p/android/issues/detail?id=7811#c6
            return methodString.equals(otherSubscriberMethod.methodString);
        } else {
            return false;
        }
    }

    private synchronized void checkMethodString() {
        if (methodString == null) {
            // Method.toString has more overhead, just take relevant parts of the method
            StringBuilder builder = new StringBuilder(64);
            builder.append(method.getDeclaringClass().getName());
            builder.append('#').append(method.getName());
            builder.append('(').append(eventType.getName());
            methodString = builder.toString();
        }
    }

    @Override
    public int hashCode() {
        return method.hashCode();
    }
}
7>:Subscription注解代码如下:
final class Subscription {
    final Object subscriber;
    final SubscriberMethod subscriberMethod;
    /**
     * Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked by queued event delivery
     * {@link EventBus#invokeSubscriber(PendingPost)} to prevent race conditions.
     */
    volatile boolean active;

    Subscription(Object subscriber, SubscriberMethod subscriberMethod) {
        this.subscriber = subscriber;
        this.subscriberMethod = subscriberMethod;
        active = true;
    }

    @Override
    public boolean equals(Object other) {
        if (other instanceof Subscription) {
            Subscription otherSubscription = (Subscription) other;
            return subscriber == otherSubscription.subscriber
                    && subscriberMethod.equals(otherSubscription.subscriberMethod);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return subscriber.hashCode() + subscriberMethod.methodString.hashCode();
    }
}
8>:ThreadMode代码如下:
/**
 * Each event handler method has a thread mode, which determines in which thread the method is to be called by EventBus.
 * EventBus takes care of threading independently from the posting thread.
 * 
 * @see EventBus#register(Object)
 * @author Markus
 */
public enum ThreadMode {
    /**
     * Subscriber will be called in the same thread, which is posting the event. This is the default. Event delivery
     * implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for
     * simple tasks that are known to complete is a very short time without requiring the main thread. Event handlers
     * using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.
     */
    /**
     * 同一个线程,在哪个线程发送事件,那么该方法就在哪个线程执行
     */
    POSTING,

    /**
     * Subscriber will be called in Android's main thread (sometimes referred to as UI thread). If the posting thread is
     * the main thread, event handler methods will be called directly. Event handlers using this mode must return
     * quickly to avoid blocking the main thread.
     */
    /**
     * 在主线程中执行
     */
    MAIN,

    /**
     * Subscriber will be called in a background thread. If posting thread is not the main thread, event handler methods
     * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single
     * background thread, that will deliver all its events sequentially. Event handlers using this mode should try to
     * return quickly to avoid blocking the background thread.
     */
    /**
     * 子线程:如果发布事件的线程是主线程,那么调用线程池中的子线程来执行订阅方法;否则直接执行;
     */
    BACKGROUND,

    /**
     * Event handler methods are called in a separate thread. This is always independent from the posting thread and the
     * main thread. Posting events never wait for event handler methods using this mode. Event handler methods should
     * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number
     * of long running asynchronous handler methods at the same time to limit the number of concurrent threads. EventBus
     * uses a thread pool to efficiently reuse threads from completed asynchronous event handler notifications.
     */
    /**
     * 异步线程:无论发布事件执行在主线程还是子线程,都利用一个异步线程来执行订阅方法。
     */
    ASYNC
}

代码已上传至github:
https://github.com/shuai999/Architect_day22.git

你可能感兴趣的:(EventBus系列文章(二) - 手写EventBus)