移动开发最新EventBus源码解析,2024年最新安卓开发面试考啥

文末

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

这里放上一部分我工作以来以及参与过的大大小小的面试收集总结出来的相关的几十套腾讯、头条、阿里、美团等公司21年的面试专题,其中把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分免费分享给大家,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家~

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

【延伸Android必备知识点】

这里只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢在关注一下~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

/** Convenience singleton for apps using a process-wide EventBus instance. */

public static EventBus getDefault() {

if (defaultInstance == null) {

synchronized (EventBus.class) {

if (defaultInstance == null) {

defaultInstance = new EventBus();

}

}

}

return defaultInstance;

}

我们可以看出这是一个典型的双重锁校验的单例模式,如果不存在则新建,接着我们来看EventBus的构造方法

/**

  • Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a

  • central bus, consider {@link #getDefault()}.

*/

public EventBus() {

this(DEFAULT_BUILDER);

}

看到这里有没有疑惑的地方?既然是单例模式为什么构造方法不是private而是public,这是因为EventBus可能有多条总线,订阅者注册到不同线上的 EventBus,通过不同的实例来发送数据,不同的 EventBus 是相互隔离开的,订阅者都只会收到注册到该线上事件。

构造方法中只有一个DEFAULT_BUILDER,接着我们来看DEFAULT_BUILDER

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

我们可以看出EventBus是通过一个EventBusBuilder的实例来构建的

EventBus(EventBusBuilder builder) {

subscriptionsByEventType = new HashMap<>();

typesBySubscriber = new HashMap<>();

stickyEvents = new ConcurrentHashMap<>();

mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);

backgroundPoster = new BackgroundPoster(this);

asyncPoster = new AsyncPoster(this);

indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;

subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,

builder.strictMethodVerification, builder.ignoreGeneratedIndex);

logSubscriberExceptions = builder.logSubscriberExceptions;

logNoSubscriberMessages = builder.logNoSubscriberMessages;

sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;

sendNoSubscriberEvent = builder.sendNoSubscriberEvent;

throwSubscriberException = builder.throwSubscriberException;

eventInheritance = builder.eventInheritance;

executorService = builder.executorService;

}

subscriptionsByEventType = new HashMap<>();

typesBySubscriber = new HashMap<>();

stickyEvents = new ConcurrentHashMap<>();

前三行是三个hashMap但有所不同:

subscriptionsByEventType 对应的hashMap是:

private final Map<Class<?>, CopyOnWriteArrayList> subscriptionsByEventType;

它的key是订阅事件,value是所有订阅者集合。当我们发送Event事件的时候都是从这个集合中去寻找,

typesBySubscriber 对应的hashMap是:

private final Map<Object, List<Class<?>>> typesBySubscriber;

它的key是订阅者对象,value是这个订阅者订阅的所有事件集合。当我们注册或者反注册的时候都是操作这个集合然后操作subscriptionsByEventType。

stickyEvents 对应的hashMap是:

private final Map<Class<?>, Object> stickyEvents;

它的key是粘性事件的class对象,value是粘性事件对象。接着我们看后面三行代码:

mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10); backgroundPoster = new BackgroundPoster(this);

asyncPoster = new AsyncPoster(this);

mainThreadPoster

我们知道,在此之前我们线程间通信或者传值的话 都是使用Hanlder,而mainThreadPoster 的本质就是一个Hanlder,我们点击mainThreadPoster 进去看

既然是hanlder所以我们主要来看它的hanleMessage方法

@Override

public void handleMessage(Message msg) {

boolean rescheduled = false;

try {

long started = SystemClock.uptimeMillis();

while (true) {

PendingPost pendingPost = queue.poll();

if (pendingPost == null) {

synchronized (this) {

// Check again, this time in synchronized

pendingPost = queue.poll();

if (pendingPost == null) {

handlerActive = false;

return;

}

}

}

eventBus.invokeSubscriber(pendingPost);

long timeInMethod = SystemClock.uptimeMillis() - started;

if (timeInMethod >= maxMillisInsideHandleMessage) {

if (!sendMessage(obtainMessage())) {

throw new EventBusException(“Could not send handler message”);

}

rescheduled = true;

return;

}

}

} finally {

handlerActive = rescheduled;

}

}

从队列中不断的去处pendingPost,使用 eventBus.invokeSubscriber(pendingPost)进行事件分发,而pendingPost是一个可复用对象的复用池,通过obtainPendingPost方法进行复用,releasePendingPost方法进行回收,这个HandlerPoster是运行在主线程中的,因为初始化的时候有个 Looper.getMainLooper()。

pendingPost主要有三个对象:事件、订阅和一个节点

backgroundPoster

backgroundPoster本质是一个Runnable,主要在后台处理事件,所以我们主要看它的run方法

public void run() {

try {

try {

while (true) {

PendingPost pendingPost = queue.poll(1000);

if (pendingPost == null) {

synchronized (this) {

// Check again, this time in synchronized

pendingPost = queue.poll();

if (pendingPost == null) {

executorRunning = false;

return;

}

}

}

eventBus.invokeSubscriber(pendingPost);

}

} catch (InterruptedException e) {

Log.w(“Event”, Thread.currentThread().getName() + " was interruppted", e);

}

} finally {

executorRunning = false;

}

}

mainThreadPoster是一样的,从复用池中取出,如果队列为空就间隔1秒再取,然后调用invokeSubscriber方法进行分发

asyncPoster

asyncPoster的本质也是一个Runnable

@Override

public void run() {

PendingPost pendingPost = queue.poll();

if(pendingPost == null) {

throw new IllegalStateException(“No pending post available”);

}

eventBus.invokeSubscriber(pendingPost);

}

但是每次只取一个pendingPost。不论发布线程是否为主线程,都使用一个空闲线程来处理。Async类的所有线程是相互独立的,因此不会出现卡线程的问题。

说到这里这三个poster都是负责线程调度的,最后都调用invokeSubscriber进行事件分发,那么我们有必要来来看下这个invokeSubscriber方法。

invokeSubscriber

/**

  • Invokes the subscriber if the subscriptions is still active. Skipping subscriptions prevents race conditions

  • between {@link #unregister(Object)} and event delivery. Otherwise the event might be delivered after the

  • subscriber unregistered. This is particularly important for main thread delivery and registrations bound to the

  • live cycle of an Activity or Fragment.

*/

void invokeSubscriber(PendingPost pendingPost) {

Object event = pendingPost.event;

Subscription subscription = pendingPost.subscription;

PendingPost.releasePendingPost(pendingPost);

if (subscription.active) {

invokeSubscriber(subscription, event);

}

}

void invokeSubscriber(Subscription subscription, Object event) {

try {

subscription.subscriberMethod.method.invoke(subscription.subscriber, event);

} catch (InvocationTargetException e) {

handleSubscriberException(subscription, event, e.getCause());

} catch (IllegalAccessException e) {

throw new IllegalStateException(“Unexpected exception”, e);

}

}

这样就比较一目了然了,从peningPost中得到事件和订阅,通过反射调用了订阅者的订阅函数并把event对象作为参数传入。

二、注册事件

======

/**

  • Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they

  • are no longer interested in receiving events.

  • Subscribers have event handling methods that must be annotated by {@link Subscribe}.

  • The {@link Subscribe} annotation also allows configuration like {@link

  • ThreadMode} and priority.

*/

public void register(Object subscriber) {

Class<?> subscriberClass = subscriber.getClass();

List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);

synchronized (this) {

for (SubscriberMethod subscriberMethod : subscriberMethods) {

subscribe(subscriber, subscriberMethod);

}

}

}

我们可以看到这个注册方法

首先获取订阅者的class对象,通过subscriberMethodFinder寻找这个class对象的所有订阅方法集合SubscriberMethod,SubscriberMethod,而SubscriberMethod中包含了相应的线程、Method对象、事件类型、优先级记忆是否是粘性事件等。

然后通过 subscribe(subscriber, subscriberMethod);订阅事件,而subscriberMethod对象是通过subscriberMethodFinder获取的,所以我们先来看下subscriberMethodFinder的实现

subscriberMethodFinder

subscriberMethodFinder类是用来查找和缓存订阅者响应函数的信息的类,那么我们如何获取订阅者响应函数的信息,这里就要提到APT运行时注解了,当然我们这里不对APT进行介绍,EventBus中是通过@Subscribe()注解来获取的,我们来看下@Subscribe()

言归正传我们来看subscriberMethodFinder方法

List findSubscriberMethods(Class<?> subscriberClass) {

List subscriberMethods = METHOD_CACHE.get(subscriberClass);

if (subscriberMethods != null) {

return subscriberMethods;

}

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;

}

}

从METHOD_CACHE取看是否有缓存,key是保存订阅类的类名,value是保存类中订阅的方法数据,如果忽略注解器生成的MyEventBusIndex类就调用反射来获取订阅类中的订阅方法信息,如果没有忽略就从注解生成的是否忽略注解器生成的MyEventBusIndex类中来获取订阅类中的订阅方法信息。

findUsingReflection

private List findUsingReflection(Class<?> subscriberClass) {

FindState findState = prepareFindState();

findState.initForSubscriber(subscriberClass);

while (findState.clazz != null) {

//通过反射来获得订阅方法信息

findUsingReflectionInSingleClass(findState);

//查找父类的订阅方法

findState.moveToSuperclass();

}

//返回订阅方法集合

return getMethodsAndRelease(findState);

}

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) {

// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149

methods = findState.clazz.getMethods();

findState.skipSuperClasses = true;

}

//遍历Method

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();

//实例化SubscriberMethod对象并添加

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");

}

}

}

findUsingInfo

findUsingInfo是通过运行时注解生成的MyEventBusIndex类来获取订阅方法信息

private List findUsingInfo(Class<?> subscriberClass) {

FindState findState = prepareFindState();

findState.initForSubscriber(subscriberClass);

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 {

findUsingReflectionInSingleClass(findState);

}

//到父类中查找

findState.moveToSuperclass();

}

return getMethodsAndRelease(findState);

}

getSubscriberInfo

结语

  • 现在随着短视频,抖音,快手的流行NDK模块开发也显得越发重要,需要这块人才的企业也越来越多,随之学习这块的人也变多了,音视频的开发,往往是比较难的,而这个比较难的技术就是NDK里面的技术。
  • 音视频/高清大图片/人工智能/直播/抖音等等这年与用户最紧密,与我们生活最相关的技术一直都在寻找最终的技术落地平台,以前是windows系统,而现在则是移动系统了,移动系统中又是以Android占比绝大部分为前提,所以AndroidNDK技术已经是我们必备技能了。
  • 要学习好NDK,其中的关于C/C++,jni,Linux基础都是需要学习的,除此之外,音视频的编解码技术,流媒体协议,ffmpeg这些都是音视频开发必备技能,而且
  • OpenCV/OpenGl/这些又是图像处理必备知识,下面这些我都是当年自己搜集的资料和做的一些图,因为当年我就感觉视频这块会是一个大的趋势。所以提前做了一些准备。现在拿出来分享给大家。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

;

}

getSubscriberInfo

结语

  • 现在随着短视频,抖音,快手的流行NDK模块开发也显得越发重要,需要这块人才的企业也越来越多,随之学习这块的人也变多了,音视频的开发,往往是比较难的,而这个比较难的技术就是NDK里面的技术。
  • 音视频/高清大图片/人工智能/直播/抖音等等这年与用户最紧密,与我们生活最相关的技术一直都在寻找最终的技术落地平台,以前是windows系统,而现在则是移动系统了,移动系统中又是以Android占比绝大部分为前提,所以AndroidNDK技术已经是我们必备技能了。
  • 要学习好NDK,其中的关于C/C++,jni,Linux基础都是需要学习的,除此之外,音视频的编解码技术,流媒体协议,ffmpeg这些都是音视频开发必备技能,而且
  • OpenCV/OpenGl/这些又是图像处理必备知识,下面这些我都是当年自己搜集的资料和做的一些图,因为当年我就感觉视频这块会是一个大的趋势。所以提前做了一些准备。现在拿出来分享给大家。

[外链图片转存中…(img-tQnwqkYq-1715448202432)]

[外链图片转存中…(img-SuNn0Obx-1715448202432)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 10
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值