前言
EventBus系列文章
EventBus系列文章(一) - register()、subscribe()、post()分析
EventBus系列文章(二) - 手写EventBus
1. 手写EventBus
从MainActivity跳转TestActivity,然后点击TestActivity,把数据会传到MainActivity即可;
2. 代码如下
1>:MainActivity代码如下:
/**
* Email: 2185134304@qq.com
* 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: 2185134304@qq.com
* 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: 2185134304@qq.com
* 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<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
// typesBySubscriber 这个集合存放的是?
// key 是所有的订阅者
// value 是所有订阅者里面方法的参数的class
private final Map<Object, List<Class<?>>> typesBySubscriber;
private EventBus(){
typesBySubscriber = new HashMap<Object, List<Class<?>>>() ;
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<SubscriberMethod> 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<Subscription> 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<Class<?>> 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<Class<?>> 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<Subscription> 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<Subscription> 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