2、接着,创建具体观察者
public class WeXinUser implements observer {
private String name;
public WeXinUser(String name) {
this.name = name;
}
@Override
public void update(String message) {
…
}
}
复制代码
3、然后,创建抽象被观察者
public interface observable {
public void addWeXinUser(WeXinUser weXinUser);
public void removeWeXinUser(WeXinUser weXinUser);
public void notify(String message);
}
复制代码
4、最后,创建具体被观察者
public class Subscription implements observable {
private List mUserList = new ArrayList();
@Override
public void addWeXinUser(WeXinUser weXinUser) {
mUserList.add(weXinUser);
}
@Override
public void removeWeXinUser(WeXinUser weXinUser) {
mUserList.remove(weXinUser);
}
@Override
public void notify(String message) {
for(WeXinUser weXinUser : mUserList) {
weXinUser.update(message);
}
}
}
复制代码
在具体使用时,我们便可以这样使用,如下所示:
Subscription subscription = new Subscription();
WeXinUser hongYang = new WeXinUser(“HongYang”);
WeXinUser rengYuGang = new WeXinUser(“RengYuGang”);
WeXinUser liuWangShu = new WeXinUser(“LiuWangShu”);
subscription.addWeiXinUser(hongYang);
subscription.addWeiXinUser(rengYuGang);
subscription.addWeiXinUser(liuWangShu);
subscription.notify(“New article coming”);
复制代码
在这里,hongYang、rengYuGang、liuWangShu等大神都订阅了我的微信公众号,每当我的公众号发表文章时(subscription.notify()),他们就会接收到最新的文章信息(weXinUser.update())。(ps:当然,这一切都是YY,事实是,我并没有微信公众号-0v0-)
当然,EventBus的观察者模式和一般的观察者模式不同,它使用了扩展的观察者模式对事件进行订阅和分发,其实这里的扩展就是指的使用了EventBus来作为中介者,抽离了许多职责,如下是它的官方原理图:
在得知了EventBus的原理之后,我们注意到,每次我们在register之后,都必须进行一次unregister,这是为什么呢?
因为register是强引用,它会让对象无法得到内存回收,导致内存泄露。所以必须在unregister方法中释放对象所占的内存。
有些同学可能之前使用的是EventBus2.x的版本,那么它又与EventBus3.x的版本有哪些区别呢?
-
1、EventBus2.x使用的是运行时注解,它采用了反射的方式对整个注册的类的所有方法进行扫描来完成注册,因而会对性能有一定影响。
-
2、EventBus3.x使用的是编译时注解,Java文件会编译成.class文件,再对class文件进行打包等一系列处理。在编译成.class文件时,EventBus会使用EventBusAnnotationProcessor注解处理器读取@Subscribe()注解并解析、处理其中的信息,然后生成Java类来保存所有订阅者的订阅信息。这样就创建出了对文件或类的索引关系,并将其编入到apk中。
-
3、从EventBus3.0开始使用了对象池缓存减少了创建对象的开销。
除了EventBus,其实现在比较流行的事件总线还有RxBus,那么,它与EventBus相比又如何呢?
-
1、RxJava的Observable有onError、onComplete等状态回调。
-
2、Rxjava使用组合而非嵌套的方式,避免了回调地狱。
-
3、Rxjava的线程调度设计的更加优秀,更简单易用。
-
4、Rxjava可使用多种操作符来进行链式调用来实现复杂的逻辑。
-
5、Rxjava的信息效率高于EventBus2.x,低于EventBus3.x。
在了解了EventBus和RxBus的区别之后,那么,对待新项目的事件总线选型时,我们该如何考量?
很简单,如果项目中使用了RxJava,则使用RxBus,否则使用EventBus3.x。
接下来将按以下顺序来进行EventBus的源码分析:
-
1、订阅者:EventBus.getDefault().register(this);
-
2、发布者:EventBus.getDefault().post(new CollectEvent());
-
3、订阅者:EventBus.getDefault().unregister(this)。
下面,我们正式开始EventBus的探索之旅~
二、EventBus.getDefault().register(this)
首先,我们从获取EventBus实例的方法getDefault()开始分析:
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
复制代码
在getDefault()中使用了双重校验并加锁的单例模式来创建EventBus实例。
接着,我们看到EventBus的默认构造方法中做了什么:
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
复制代码
在EventBus的默认构造方法中又调用了它的另一个有参构造方法,将一个类型为EventBusBuilder的DEFAULT_BUILDER对象传递进去了。这里的EventBusBuilder很明显是一个EventBus的建造器,以便于EventBus能够添加自定义的参数和安装一个自定义的默认EventBus实例。
我们再看一下EventBusBuilder的构造方法:
public class EventBusBuilder {
…
EventBusBuilder() {
}
…
}
复制代码
EventBusBuilder的构造方法中什么也没有做,那我么继续查看EventBus的这个有参构造方法:
private final Map<Class<?>, CopyOnWriteArrayList> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
private final Map<Class<?>, Object> stickyEvents;
EventBus(EventBusBuilder builder) {
…
// 1
subscriptionsByEventType = new HashMap<>();
// 2
typesBySubscriber = new HashMap<>();
// 3
stickyEvents = new ConcurrentHashMap<>();
// 4
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
…
// 5
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
// 从builder取中一些列订阅相关信息进行赋值
…
// 6
executorService = builder.executorService;
}
复制代码
在注释1处,创建了一个subscriptionsByEventType对象,可以看到它是一个类型为HashMap的subscriptionsByEventType对象,并且其key为 Event 类型,value为 Subscription链表。这里的Subscription是一个订阅信息对象,它里面保存了两个重要的字段,一个是类型为 Object 的 subscriber,该字段即为注册的对象(在 Android 中时通常是 Activity对象);另一个是 类型为SubscriberMethod 的 subscriberMethod,它就是被@Subscribe注解的那个订阅方法,里面保存了一个重要的字段eventType,它是 Class<?> 类型的,代表了 Event 的类型。在注释2处,新建了一个类型为 Map 的typesBySubscriber对象,它的key为subscriber对象,value为subscriber对象中所有的 Event 类型链表,日常使用中仅用于判断某个对象是否注册过。在注释3处新建了一个类型为ConcurrentHashMap的stickyEvents对象,它是专用于粘性事件处理的一个字段,key为事件的Class对象,value为当前的事件。可能有的同学不了解sticky event,这里解释下:
- 我们都知道普通事件是先注册,然后发送事件才能收到;而粘性事件,在发送事件之后再订阅该事件也能收到。并且,粘性事件会保存在内存中,每次进入都会去内存中查找获取最新的粘性事件,除非你手动解除注册。
在注释4处,新建了三个不同类型的事件发送器,这里总结下:
-
mainThreadPoster:主线程事件发送器,通过它的mainThreadPoster.enqueue(subscription, event)方法可以将订阅信息和对应的事件进行入队,然后通过 handler 去发送一个消息,在 handler 的 handleMessage 中去执行方法。
-
backgroundPoster:后台事件发送器,通过它的enqueue() 将方法加入到后台的一个队列,最后通过线程池去执行,注意,它在 Executor的execute()方法 上添加了 synchronized关键字 并设立 了控制标记flag,保证任一时间只且仅能有一个任务会被线程池执行。
-
asyncPoster:实现逻辑类似于backgroundPoster,不同于backgroundPoster的保证任一时间只且仅能有一个任务会被线程池执行的特性,asyncPoster则是异步运行的,可以同时接收多个任务。
我们再回到注释5这行代码,这里新建了一个subscriberMethodFinder对象,这是从EventBus中抽离出的订阅方法查询的一个对象,在优秀的源码中,我们经常能看到组合优于继承的这种实现思想。在注释6处,从builder中取出了一个默认的线程池对象,它由Executors的newCachedThreadPool()方法创建,它是一个有则用、无则创建、无数量上限的线程池。
分析完这些核心的字段之后,后面的讲解就比较轻松了,接着我们查看EventBus的regist()方法:
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
// 1
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
// 2
subscribe(subscriber, subscriberMethod);
}
}
}
复制代码
在注释1处,根据当前注册类获取 subscriberMethods这个订阅方法列表 。在注释2处,使用了增强for循环令subsciber对象 对 subscriberMethods 中每个 SubscriberMethod 进行订阅。
接着我们查看SubscriberMethodFinder的findSubscriberMethods()方法:
List findSubscriberMethods(Class<?> subscriberClass) {
// 1
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
// 2
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
- " and its super classes have no public methods with the @Subscribe annotation");
} else {
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
复制代码
在注释1处,如果缓存中有subscriberClass对象对应 的订阅方法列表,则直接返回。注释2处,先详细说说这个ignoreGeneratedIndex字段, 它用来判断是否使用生成的 APT 代码去优化寻找接收事件的过程,如果开启了的话,那么将会通过 subscriberInfoIndexes 来快速得到接收事件方法的相关信息。如果我们没有在项目中接入 EventBus 的 APT,那么可以将 ignoreGeneratedIndex 字段设为 false 以提高性能。这里ignoreGeneratedIndex 默认为false,所以会执行findUsingInfo()方法,后面生成 subscriberMethods 成功的话会加入到缓存中,失败的话会 抛出异常。
接着我们查看SubscriberMethodFinder的findUsingInfo()方法:
private List findUsingInfo(Class<?> subscriberClass) {
// 1
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
// 2
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod: array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
// 3
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
// 4
return getMethodsAndRelease(findState);
}
复制代码
在注释1处,调用了SubscriberMethodFinder的prepareFindState()方法创建了一个新的 FindState 类,我们来看看这个方法:
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
private FindState prepareFindState() {
// 1
synchronized(FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
FindState state = FIND_STATE_POOL[i];
if (state != null) {
FIND_STATE_POOL[i] = null;
return state;
}
}
}
// 2
return new FindState();
}
复制代码
在注释1处,会先从 FIND_STATE_POOL 即 FindState 池中取出可用的 FindState(这里的POOL_SIZE为4),如果没有的话,则通过注释2处的代码直接新建 一个新的 FindState 对象。
接着我们来分析下FindState这个类:
static class FindState {
…
void initForSubscriber(Class<?> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
…
}
复制代码
它是 SubscriberMethodFinder 的内部类,这个方法主要做一个初始化、回收对象等工作。
我们接着回到SubscriberMethodFinder的注释2处的SubscriberMethodFinder()方法:
private SubscriberInfo getSubscriberInfo(FindState findState) {
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index: subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
复制代码
在前面初始化的时候,findState的subscriberInfo和subscriberInfoIndexes 这两个字段为空,所以这里直接返回 null。
接着我们查看注释3处的findUsingReflectionInSingleClass()方法:
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
for (Method method: methods) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?> [] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
// 重点
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + “.” + method.getName();
throw new EventBusException("@Subscribe method " + methodName + "must have exactly 1 parameter but has " + parameterTypes.length);
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + “.” + method.getName();
throw new EventBusException(methodName + " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}
复制代码
这个方法很长,大概做的事情是:
-
1、通过反射的方式获取订阅者类中的所有声明方法,然后在这些方法里面寻找以 @Subscribe作为注解的方法进行处理。
-
2、在经过经过一轮检查,看看 findState.subscriberMethods是否存在,如果没有,将方法名,threadMode,优先级,是否为 sticky 方法等信息封装到 SubscriberMethod 对象中,最后添加到 subscriberMethods 列表中。
最后,我们继续查看注释4处的getMethodsAndRelease()方法:
private List getMethodsAndRelease(FindState findState) {
// 1
List subscriberMethods = new ArrayList<>(findState.subscriberMethods);
// 2
findState.recycle();
// 3
synchronized(FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
if (FIND_STATE_POOL[i] == null) {
FIND_STATE_POOL[i] = findState;
break;
}
}
}
// 4
return subscriberMethods;
}
复制代码
在这里,首先在注释1处,从findState中取出了保存的subscriberMethods。在注释2处,将findState里的保存的所有对象进行回收。在注释3处,把findState存储在 FindState 池中方便下一次使用,以提高性能。最后,在注释4处,返回subscriberMethods。接着,在EventBus的 register() 方法的最后会调用 subscribe 方法:
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
复制代码
我们继续看看这个subscribe()方法做的事情:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
// 1
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList <> ();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType);
}
}
int size = subscriptions.size();
// 2
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
// 3
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
// 4
if (subscriberMethod.sticky) {
if (eventInheritance) {
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if(eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
复制代码
最后附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总)
面试成功其实是必然的,因为我做足了充分的准备工作,包括刷题啊,看一些Android核心的知识点,看一些面试的博客吸取大家面试的一些经验,下面这份PDF是我翻阅了差不多1个月左右一些Android大博主的博客从他们那里取其精华去其糟泊所整理出来的一些Android的核心知识点, 全部都是精华中的精华,我能面试到现在资深开发人员跟我整理的这本Android核心知识点有密不可分的关系,在这里本着共赢的心态分享给各位朋友。
这份PDF囊括了JVM,Java集合,Java多线程并发,Java基础,生命周期,微服务, 进程,Parcelable 接口,IPC,屏幕适配,线程异步,ART,架构,Jetpack,NDK开发,计算机网络基础,类加载器,Android 开源库源码分析,设计模式汇总,Gradle 知识点汇总…
由于篇幅有限,就不做过多的介绍,大家请自行脑补
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
lse {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
复制代码
最后附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总)
[外链图片转存中…(img-iLKJ452q-1714358327625)]
面试成功其实是必然的,因为我做足了充分的准备工作,包括刷题啊,看一些Android核心的知识点,看一些面试的博客吸取大家面试的一些经验,下面这份PDF是我翻阅了差不多1个月左右一些Android大博主的博客从他们那里取其精华去其糟泊所整理出来的一些Android的核心知识点, 全部都是精华中的精华,我能面试到现在资深开发人员跟我整理的这本Android核心知识点有密不可分的关系,在这里本着共赢的心态分享给各位朋友。
[外链图片转存中…(img-aDbWfqF3-1714358327626)]
这份PDF囊括了JVM,Java集合,Java多线程并发,Java基础,生命周期,微服务, 进程,Parcelable 接口,IPC,屏幕适配,线程异步,ART,架构,Jetpack,NDK开发,计算机网络基础,类加载器,Android 开源库源码分析,设计模式汇总,Gradle 知识点汇总…
由于篇幅有限,就不做过多的介绍,大家请自行脑补
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!