注解
注解的基础定义,很多文章都有了,在这里就不总结了。
推荐一个文章大致了解:</br>
https://blog.csdn.net/niubitianping/article/details/60145128
之前的疑惑
之前看过一些文章,说注解IOC框架一些思路在Android上会引起性能的损耗,以前疑惑为什么会这样?
再查相关的资料的时候,是说因为用到了反射,进而引起了性能损耗。那为什么反射会引起性能的损耗呢? 是通过 EventBus 来理解了这个原因。实际场景分析,如果有不对的希望有大神看到可以指点一二。
早期版本的EventBus原理
主要是通过一段段摘出源码进行分析理解。通过删除cpoy来进行理解,并非全是源码</br>
一、首先就是EventBus的简单使用示例
EventBus.getDefault().register(this);
EventBus.getDefault().unregister(this);
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(Object object) {
}
先跟入在需要接收Event的地方的注册流程 register 。
public void register(Object subscriber) {
//通过getClass拿到注册类的class文件
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> mSubscriberMethodFinder = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
subscriberMethods = findUsingReflection(subscriberClass);
if (subscriberMethods.isEmpty()) {
//throw new EventBusException("Subscriber " + subscriberClass
// + " and its super classes have no public methods with the @Subscribe annotation");
return null;
} else {
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
在 register 中通过 getClass 拿到 class 文件。在 findSubscriberMethods 方法中找到对应的 SubscriberMethod 的集合。SubscriberMethod 是用来保存 Subscriber 反射拿到的 Method 、 ThreadMode(在什么线程Post Event)、EventTpye的class、以及优先级。</br>
之前刚开始的时候对理解源码中 SubscriberMethod 中的命名有点问题。源码中属性 final Class<?> eventType;
起初以为是通过反射拿到 Method中的参数(实际上也是拿到参数),看到后边就明白了,本地的队列、缓存其实是通过 eventType 来进行区分,这里也就是事件类型。
好接着往下看,看到方法名 findUsingReflection
这里就是通过反射拿到 subscriberClass 相关注解方法的地方。简化过后的代码
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
Method[] methods = subscriberClass.getDeclaredMethods();
List<SubscriberMethod> subscribeMethods = new ArrayList<>();
for (Method method : methods) {
//获取标记有自定义注解 Subscribe 的方法
Subscribe annotation = method.getAnnotation(Subscribe.class);
if(null != annotation){
//获取方法参数
Class<?>[] parameterTypes = method.getParameterTypes();
if(parameterTypes.length == 1){
//只有一个参数的时候获取一个参数
Class<?> parameterType = parameterTypes[0];
//获取自定义注解Subscribe上的value,即时在哪个线程进行
ThreadMode threadMode = annotation.threadMode();
subscribeMethods.add(new SubscriberMethod(method,threadMode,parameterType));
}
}
}
return subscribeMethods;
}
在这里看到一个损耗性能的地方,代码是通过 for循环遍历整个 subscriber 的Method,并且判断是否有 Subscribe的注解标记。(但是感觉这里不是最终的损耗点,希望有大神指点出来具体的原因)
最后在通过一些列的代码操作,进行本地保存
/**
* 简单的注册流程
* @param subscriber
* @param subscriberMethod
*/
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//通过反射获取得到的方法的参数类型来保存subscriber?? 也就是事件类型
Class<?> parameterType = subscriberMethod.mEvnetType;
CopyOnWriteArrayList<Subscription> subscriptions = mSubscriberByEventType.get(parameterType);
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
if(subscriptions == null){
subscriptions = new CopyOnWriteArrayList<>();
mSubscriberByEventType.put(parameterType,subscriptions);
}else{
if(subscriptions.contains(newSubscription)){
//存在则不用再次注册
return;
}
}
subscriptions.add(newSubscription);
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(parameterType);
}
mSubscriberByEventType 是一个Map集合。就是用 eventType 作为Key,CopyOnWriteArrayList<Subscription> 作为Value 进行存储。
最后通过 post 发送Event。
public void post(Object event){
//将event添加到队列中
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if(!postingState.isPosting){
//postingState.isMainThread = isMainThread();
postingState.isPosting = true;
try {
while (!eventQueue.isEmpty()) {
//从队列中获取event,并且根据 threadMode 在那个线程发送 Event
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
//获取 event class 文件
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
// if (!subscriptionFound) {
// if (logNoSubscriberMessages) {
// logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
// }
// if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
// eventClass != SubscriberExceptionEvent.class) {
// post(new NoSubscriberEvent(this, event));
// }
// }
}
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = mSubscriberByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
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;
}
//这里是截取的源码的一部分,主要的post逻辑在这里进行执行
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.mThreadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.mThreadMode);
}
}
//到这里就可以看出来,通过反射拿到Method之后,在 invoke 执行该方法,将 Event传送出去
void invokeSubscriber(Subscription subscription, Object event) {
try {
//最终在这里通过反射拿到 方法和ThreadMode之后 invoke 执行方法,最终将方Event传到对应的 Subscribe的标记方法中
subscription.subscriberMethod.mMethod.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
//handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
最后思考
单从源码这里看到,通过注解、反射拿到 Subscribe 中的标记方法,就存在在遍历整个 Subscribe 过程,因此感觉是这块拖慢了 APP 的性能。
网友评论