先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
正文
// 这里采用了享元设计模式
FindState findState = prepareFindState();
// 初始化FindState里的参数
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);
}
// 获取FindState对象采用了享元模式
private FindState prepareFindState() {
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;
}
}
}
return new FindState();
}
// FindState类定义的信息
static class FindState {
final List subscriberMethods = new ArrayList<>();
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);
Class<?> subscriberClass;
Class<?> clazz;
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;
void initForSubscriber(Class<?> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
…
…
}
这里先获取了FindState对象,之后调用了 findState.initForSubscriber(subscriberClass)
方法,只是为了给FindState对象里的一些信息进行赋值操作。再然后是一个 while循环,循环里首先调用getSubscriberInfo(findState)
方法,点进去看一下,发现该方法返回null。这里留一个小疑问,getSubscriberInfo()方法什么时候不为null?? 答案留到最后。
private SubscriberInfo getSubscriberInfo(FindState findState) {
// findState.subscriberInfo在初始化的时候置为null,所以该if 分支不会执行
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
// subscriberInfoIndexes初始化的时候也是null,并没有赋值
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
所以findUsingInfo()
的while循环里直接走了else分支:findUsingReflectionInSingleClass(findState)
。
也就是说findUsingInfo()方法的主要逻辑是由findUsingReflectionInSingleClass()方法去完成的(默认情况,不考虑使用apt)
这里有个细节要看一下:
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();
}
这个while循环什么时候结束呢?这是我们第一次看EventBus源码看到这里比较疑惑的地方,答案就在这个 findState.moveToSuperclass()
里面:
void moveToSuperclass() {
if (skipSuperClasses) {
clazz = null;
} else {
clazz = clazz.getSuperclass();
String clazzName = clazz.getName();
/** Skip system classes, this just degrades performance. */
if (clazzName.startsWith(“java.”) || clazzName.startsWith(“javax.”) || clazzName.startsWith(“android.”)) {
clazz = null;
}
}
}
我们可以看到这里clazz赋值为它的超类,直到没有父类为止,才返回clazz=null
,循环也才终止。也就是说 这个while循环保证了,获取注解的方法不仅会从当前注册对象里去找,也会去从他的父类查找。
好了,继续分析findUsingReflectionInSingleClass(findState)
方法:
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;
}
// 执行遍历操作
for (Method method : methods) {
// 获取该方法的修饰符,即public、private等
int modifiers = method.getModifiers();
// 修饰符是public才会走该分支
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
// 这里是获取该方法的参数类型,String,in
Class<?>[] parameterTypes = method.getParameterTypes();
// 只有一个参数会走该分支
if (parameterTypes.length == 1) {
// 如果该方法被@subscribe注解会走该分支
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
// 获取传入的对象的Class
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");
}
}
}
findUsingReflectionInSingleClass()方法首先通过反射去拿到当前注册对象的所有的方法,然后去进行遍历,并进行第一次过滤,只针对修饰符是Public的方法,之后进行了第二次过滤,判断了方法的参数的个数是不是只有一个,如果满足,才去进一步的获取被@subscribe注解的方法。
然后调用
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,subscribeAnnotation.priority(), subscribeAnnotation.sticky()))
这行代码,new了一个SubscriberMethod()对象,传入参数,并添加到 findState.subscriberMethods的集合中去.
static class FindState {
final List subscriberMethods = new ArrayList<>();
}
之后,findUsingInfo()
的getMethodsAndRelease(findState)
方法回去获取刚刚设置的findState
的subscriberMethods
集合,并把它return出去。代码如下:
private List getMethodsAndRelease(FindState findState) {
// 对subscriberMethods进行了赋值,return出去
List subscriberMethods = new ArrayList<>(findState.subscriberMethods);
// 进行了回收
findState.recycle();
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
if (FIND_STATE_POOL[i] == null) {
FIND_STATE_POOL[i] = findState;
break;
}
}
}
return subscriberMethods;
}
步骤1总结:至此,以上就是EventBus获取一个注册对象的所有的被@subscribe注解的方法的集合的一个过程。该过程的主要方法流程为:
(1) subscriberMethodFinder.findSubscriberMethods()
(2) findUsingInfo()
(3) findUsingReflectionInSingleClass()
步骤2
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
通过步骤1,我们已经拿到了注册对象的所有的被@subscribe注解的方法的集合的。现在我们看看subscribe()都做了哪些 操作。
我们不妨想想,如果我们要去做subscribe()时,我们要考虑哪些问题,第一个问题是,要判断一下这些方法是不是已经注册过该事件了要不要考虑方法名是不是相同的问题。第二个问题是一个注册对象中有多个方法注册了该事件,我们该怎么保存这些方法,比如说事件类型是String,一个Activity里有两个方法注册了该事件,分别是onEvent1和onEvent2,那我是不是应该用一个Map集合,以事件类型为key,把onEvent1和onEvent2放到一个List集合中,把该List集合作为value。
subscribe()方法:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 拿到事件event类型,比如是String或者自定义的对象
Class<?> eventType = subscriberMethod.eventType;
// Subscription将注册对象和subscriberMethod 做为参数传入
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
// subscriptionsByEventType是一个Map集合,key是事件类型,验证了我上面的猜想
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
// 如果subscriptions是null,则new出一个CopyOnWriteArrayList,并且往Map集合中添加
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
// 这里做了if语句判断,判断一下List集合中是否存在,存在就抛异常
// 如果不存在?怎么没有add操作? 保持疑问
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
- eventType);
}
}
以上的操作验证了我之前的猜想,通过if (subscriptions.contains(newSubscription)) 这个if语句判断 是否发生了重复注册,注意这里重复注册的含义是 事件类型一致,以及方法名也一致。
接下来我们看看如果一个注册对象重复注册了事件Event(方法名不能一致),优先级priority是如何设置的
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
// 这里判断subscriberMethod的优先级是否是大于集合中的subscriberMethod的优先级,如果是,把newSubscription插进去
// 这也表明了subscription中priority大的在前,这样在事件分发时就会先获取。
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
if语句的条件subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) ,保证了subscription中priority大的在前。同时i == size 这个条件也保证了priority小的也会添加到subscriptions集合中去
紧接着我们看看EventBus是如何处理粘性事件的:
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List).
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);
}
}
}
注意以上代码有四行比较重要的注释信息。大致的意思是必须考虑eventType所有子类的现有粘性事件,在迭代的过程中,所有的event可能会因为大量的sticky events变得低效,为了使得查询变得高效应该改变数据结构。
isAssignableFrom方法的意思是判断candidateEventType是不是eventType的子类或者子接口,如果postSticky()的参数是子Event,那么@Subscribe注解方法中的参数是父Event也可以接收到此消息。
拿到粘性Event后,调用了checkPostStickyEventToSubscription()
方法,改方法内部方法内部调用了postToSubscription()
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
// --> Strange corner case, which we don’t take care of here.
postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
}
}
步骤2总结:至此,EventBus的注册操作已经全部分析完了,需要注意的是,粘性事件是在subscribe中进行post的
(二) 发送事件:EventBus.getDefault().post(xxx);
普通Event
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List eventQueue = postingState.eventQueue;
// 将Event添加到List集合中去
eventQueue.add(event);
if (!postingState.isPosting) {
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException(“Internal error. Abort state was not reset”);
}
try {
// 遍历这个list集合,条件是集合是否是空的
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
首先将当前的 Event添加到eventQueue
中去,并且while循环,处理post每一个Event事件,调用的是 postSingleEvent(eventQueue.remove(0), postingState)
:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
// 获取Event的Class对象
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
// eventInheritance初始化的时候值为true,所以会走该分支
if (eventInheritance) {
// 获取当前的Event的Class对象的所有父类的Class对象集合,优先从缓存里读取。
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
…
…
}
这里lookupAllEventTypes()
方法也是为了获取当前的Event的Class对象的所有父类的Class对象集合,优先从缓存里读取。之后是 for循环获取到的Class对象集合,调用postSingleEventForEventType()
方法:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList subscriptions;
synchronized (this) {
// subscriptionsByEventType该map是在subscribe()方法中进行了put操作
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
// 进行for循环并调用了postToSubscription()
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
postSingleEventForEventType()
方法,主要是获取Event的Class对象所对应的一个List集合,集合的对象是Subscription参数。subscriptionsByEventType对象是在subscribe()方法中进行了赋值。for循环CopyOnWriteArrayList集合,并调用postToSubscription()
线程模型
等执行到postToSubscription()
方法时,线程模型才派上了用场。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
第一个分支:线程模型是POSTING,直接调用了invokeSubscriber()
方法。
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);
}
}
很明显的看到,这是基于反射去调用方法,invoke方法接收两个参数,第一个参数是注册的对象,第二个参数是事件的Event。
从这里就可以看出来,POST并没有去做线程的调度什么的,事件处理函数的线程跟发布事件的线程在同一个线程。
第二个分支:线程模型是MAIN 首先判断了下事件发布的线程是不是主线程,如果是,执行invokeSubscriber()
方法,invokeSubscriber()
上面已经分析过,如果不是主线程,执行mainThreadPoster.enqueue(subscription, event)
。mainThreadPoster
是继承自Handler
,从这里大概可以猜到,这一步是去做线程调度的。/font>
我们看一看mainThreadPoster
的enqueue
做了什么事:
void enqueue(Subscription subscription, Object event) {
// 封装了一个PendIngPost
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
// 将PendIngPost压入队列
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
// 调用了sendMessage()
最后
今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。
最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司20年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
【Android核心高级技术PDF文档,BAT大厂面试真题解析】
【算法合集】
【延伸Android必备知识点】
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
vent);
synchronized (this) {
// 将PendIngPost压入队列
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
// 调用了sendMessage()
最后
今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。
最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司20年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
【Android核心高级技术PDF文档,BAT大厂面试真题解析】
[外链图片转存中…(img-Abwv4JbQ-1713471869817)]
【算法合集】
[外链图片转存中…(img-uMUf8goL-1713471869817)]
【延伸Android必备知识点】
[外链图片转存中…(img-nzDjZi8g-1713471869817)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-pglYDiSM-1713471869818)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!