美文网首页Android开发经验谈Android技术知识Android开发
这可能是最详细的 EventBus 源码分析05 - Subsc

这可能是最详细的 EventBus 源码分析05 - Subsc

作者: __Y_Q | 来源:发表于2020-12-09 19:12 被阅读0次

这可能是最详细的 EventBus 源码分析01 - EventBus 对象的创建
这可能是最详细的 EventBus 源码分析02 - EventBus 的注册(上篇)
这可能是最详细的 EventBus 源码分析03 - EventBus 的注册(下篇)
这可能是最详细的 EventBus 源码分析04 - 事件的发送与执行
这可能是最详细的 EventBus 源码分析05 - Subscriber Index

回顾我们之前分析的 EventBus 的注册流程, 主要是在项目运行时通过反射来查找订阅事件的信息, 这也是默认的实现方式. 但是如果项目中有大量的订阅事件的方法, 必然会对项目运行时的性能产生影响. 为了解决这个问题, EventBus在 3.0 以后提供了在项目编译时通过注解处理器去查找订阅事件方法的方式来生成一个辅助的索引类来保存这些信息, 这个索引类就是 SubscriberIndex.

(关于注解处理器的基本使用可以参照 一个例子理解 Butterknife 基本原理 这篇文章)

在分析使用索引类的流程之前, 先看是怎么使用它的.

1. 在 app 的 build.gradle 中的 defaultConfig 内添加以下配置

defaultConfig {
    ...
    javaCompileOptions{
        annotationProcessorOptions{
            arguments = [eventBusIndex: 'com.example.eventbus_diy.MyEventBusIndex']
        }
    }
}

指定要生成的辅助索引类的包名与路径. MyEventBusIndex 类名可以随意取.

 

2. 接着在 dependencies 中引入注解处理器

dependencies {
  ...
  implementation 'org.greenrobot:eventbus:3.2.0'
  annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.2.0'
}

 

3. 最后在 Application 中添加我们指定的索引类. 使用指定的索引类来生成默认的 EventBus 单例.

public class MyApplication extends Application {
  @Override
  public void onCreate() {
      super.onCreate();
      EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
  }
}

 
 
做了以上 3 步之后, 还是无法编译通过的. Application 中的 new MyEventBusIndex() 会飘红. 我们需要定义一个订阅方法后才能编译通过, 我们分别在两个 Activity 中注册 EventBus 和订阅方法

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        EventBus.getDefault().register(this);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onStringEventTest(String str) {
    }

    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void onIntegerEventTest(Integer integer) {
    }
}

public class SecondActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        EventBus.getDefault().register(this);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onStringEventTest(String str) {

    }
}

接着就可以编译通过了, 也会生成我们指定的文件. 路径为: build/generated/ap_generated_sources/debug/out/com/example/eventbus_diy/MyEventBusIndex.java

image.png

看一下为我们自动生成的索引类

public class MyEventBusIndex implements SubscriberInfoIndex {
    //用来存放订阅者与订阅者内部方法的 map, 暂且称呼它为 订阅者索引
    //Key : 订阅者的 Class 对象.  
    //Value : 实现了SubscriberInfo  接口的 SimpleSubscriberInfo  类.
    //每个 SimpleSubscriberInfo  都代表了一个订阅者内部的所有订阅信息
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

    static {
        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
        // SimpleSubscriberInfo 构造函数参数为:  
        // Class subscriberClass: 订阅者的 class 对象
        // boolean shouldCheckSuperclass: 是否检查父类 
        // SubscriberMethodInfo[] methodInfos: 订阅者内部的订阅方法数组长度是根据我们订阅者内的订阅方法的个数来定的. 
        //     SubscriberMethodInfo : 包含了订阅方法的方法名, 事件类型, 线程模型等一系列和订阅方法相关的属性.
        putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onStringEventTest", String.class, ThreadMode.MAIN),
            new SubscriberMethodInfo("onIntegerEventTest", Integer.class, ThreadMode.BACKGROUND),
        }));

        putIndex(new SimpleSubscriberInfo(SecondActivity.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onStringEventTest", String.class, ThreadMode.MAIN),
        }));

    }

    private static void putIndex(SubscriberInfo info) {
        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
    }

    // 从订阅者索引map中获取指定订阅者 key 的对应 value.
    @Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }
    }
}
// SimpleSubscriberInfo 类存储了订阅方法集合
public class SimpleSubscriberInfo extends AbstractSubscriberInfo {
    private final SubscriberMethodInfo[] methodInfos;

    public SimpleSubscriberInfo(Class subscriberClass, boolean shouldCheckSuperclass, SubscriberMethodInfo[] methodInfos) {
        super(subscriberClass, (Class)null, shouldCheckSuperclass);
        this.methodInfos = methodInfos;
    }

    public synchronized SubscriberMethod[] getSubscriberMethods() {
        int length = this.methodInfos.length;
        SubscriberMethod[] methods = new SubscriberMethod[length];
        //将每个 SubscriberMethodInfo 转化为 SubscriberMethod, 
        for(int i = 0; i < length; ++i) {
            SubscriberMethodInfo info = this.methodInfos[i];
            methods[i] = this.createSubscriberMethod(info.methodName, info.eventType, info.threadMode, info.priority, info.sticky);
        }

        return methods;
    }
}

也就说明了, 在编译阶段 , 就已拿到了所有的订阅者与订阅方法存在了 MyEventBusIndex. SUBSCRIBER_INDEX 这个 Map 中.

接下来的使用就和以前我们默认使用 EventBus 就没什么区别了.
其实我们上面做的那几步, 只是改变了 EventBus.getDefault() 返回的单例对象, 没有再使用默认返回的. 也改变了register 的方式.没有使用运行时反射获取订阅方法. 现在一起来分析一下到底是如何改变的. 先进入 EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus() 中的 builder() 方法


1. EventBus.builder()

public static EventBusBuilder builder() {
    return new EventBusBuilder();
}

这点和之前使用默认的方式都是一样的, 都是创建了一个 EventBusBuilder(). 直接看下面的 addIndex(new MyEventBusIndex())


2. EventBusBuilder.addIndex(SubscriberInfoIndex index)

public class EventBusBuilder {
  ...
  List<SubscriberInfoIndex> subscriberInfoIndexes;    
  ...
  public EventBusBuilder addIndex(SubscriberInfoIndex index) {
        if (this.subscriberInfoIndexes == null) {
            this.subscriberInfoIndexes = new ArrayList();
        }

        this.subscriberInfoIndexes.add(index);
        return this;
    }
  ...
}

参数 SubscriberInfoIndex 实际上就是自动生成的 MyEventBusIndex

MyEventBusIndex 对象添加到 EventBusBuilder.subscriberInfoIndexes 集合中. 接着又调用
EventBusBuilder. installDefaultEventBus() 方法


3. EventBusBuilder.installDefaultEventBus()

public class EventBusBuilder {
  ...
  public EventBus installDefaultEventBus() {
      Class var1 = EventBus.class;
      synchronized(EventBus.class) {
          if (EventBus.defaultInstance != null) {
              throw new EventBusException("Default instance already exists. It may be only set once before it's used the first time to ensure consistent behavior.");
          } else {
              EventBus.defaultInstance = this.build();
              return EventBus.defaultInstance;
          }
      }
  }

  public EventBus build() {
      return new EventBus(this);
  }
  ...
}

public class EventBus {
    ...
  EventBus(EventBusBuilder builder) {
        ...
        //未使用索引类就不会执行第 2 步,也就是不会调用 addIndex 方法. 所以 subscriberInfoIndexes 肯定为 null, indexCount = 0
        //使用了索引类后 indexCount =  builder.subscriberInfoIndexes.size() .
        this.indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        //未使用索引类 构造函数传入的第一个参数为 null.
        this.subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes, builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        ...
    }
  ...
}

这个其实就是根据当前 EventBusBuilder, 来创建一个 EventBus 对象并赋值给 EventBus.defaultInstance. 这样我们通过 EventBusBuilder 配置的索引类也就传递到了 EventBus 实例中 .

没有使用索引类的时候, 是在 EventBus.getDefault() 阶段创建的 EventBus 单例对象并赋值给 EventBus.defaultInstance.

接下来就是正常的 EventBus.getDefault().register(this) 流程了. 看看和默认的有什么不一样.


4. EventBus.getDefault()

static volatile EventBus defaultInstance;
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public static EventBus getDefault() {
    EventBus instance = defaultInstance;
    if (instance == null) {
        Class var1 = EventBus.class;
        synchronized(EventBus.class) {
            instance = defaultInstance;
            if (instance == null) {
                instance = defaultInstance = new EventBus();
            }
        }
    }

    return instance;
}

public EventBus() {
    this(DEFAULT_BUILDER);
}

EventBus(EventBusBuilder builder) {
  ...
}

EventBus instance = defaultInstance; 这行代码就会分两种情况了.

  • 未使用索引类的时候 defaultInstance 肯定是 null. 接着通过默认的 DEFAULT_BUILDER 去创建 EventBus 单例对象.
  • 使用了索引类后, 在第3 步中已经给 defaultInstance 赋值了. 直接返回.

那么接着就是 register() 过程了. 在 register() 的过程中大部分都和默认的相同, 区别只在于查找订阅者内部所有订阅方法的时候不同. 我们现在直接看 SubscriberMethodFinder.findUsingInfo(Class<?> subscriberClass) 这个方法


5. SubscriberMethodFinder.findUsingInfo(Class<?> subscriberClass)

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
  ...
  for(; findState.clazz != null; findState.moveToSuperclass()) {
      findState.subscriberInfo = this.getSubscriberInfo(findState);
      if (findState.subscriberInfo != null) {
          SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
          SubscriberMethod[] var4 = array;
          int var5 = array.length;

          for(int var6 = 0; var6 < var5; ++var6) {
              SubscriberMethod subscriberMethod = var4[var6];
              if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                  findState.subscriberMethods.add(subscriberMethod);
              }
          }
      } else {
          this.findUsingReflectionInSingleClass(findState);
      }
  }
  ...
  private SubscriberInfo getSubscriberInfo(SubscriberMethodFinder.FindState findState) {
      ...
      // 在第3步在 EventBusBuilder 中创建 EventBus 单例对象的时候, 调用了 EventBus 的有参构造. 
      // 传入的 EventBusBuilder 中 subscriberInfoIndexes 是有值的, 所以这里条件成立. 进入 if 内
      if (this.subscriberInfoIndexes != null) {
          Iterator var5 = this.subscriberInfoIndexes.iterator();
           //以订阅者的 class 对象为 key, 从我们索引类中的订阅者索引map 中获取对应的订阅信息并返回.
          while(var5.hasNext()) {
              SubscriberInfoIndex index = (SubscriberInfoIndex)var5.next();
              SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
              if (info != null) {
                  return info;
              }
          }
      }
      return null;
  }
...
}

之前默认的流程是在执行 findState.subscriberInfo = this.getSubscriberInfo(findState) 这行代码的时候返回的是 null. 然后就直接执行了 this.findUsingReflectionInSingleClass(findState) 通过反射去获取订阅方法.

那现在我们使用了索引类, 这里就不会返回 null 了, 而是返回了编译时通过反射获取的订阅者相关信息 SimpleSubscriberInfo赋值给 findState.subscriberInfo .接着调用 getSubscriberMethods() 获取当前订阅者内部的所有订阅方法.(在第 3 步中有说明)

然后调用 findState.checkAdd() 方法检查是否可以添加到 findState.anyMethodByEventType 中. 即是否在 findStateanyMethodByEventType 已经添加过以当前 eventTypekey 的键值对,没添加过返回true, 然后就和默认反射获取订阅方法一样将每个订阅方法都保存在 findState.subscriberMethods 中.

checkAdd() 方法在 这可能是最详细的 EventBus 源码分析02 - EventBus 的注册(上篇) 中的 1.1.2 有分析

剩下的流程就和默认不使用索引类的一样了.


总结: 索引类 Subscriber Index 的核心就是在项目编译时使用注解处理器生成保存事件订阅信息的索引类, 然后项目运行时将索引类实例设置到 EventBus 中, 这样当我们注册 EventBus 时, 从索引类取出当前注册类对应的事件订阅方法信息, 以完成最终的注册, 避免了运行时反射处理的过程, 所以在性能上会有质的提高. 项目中可以根据实际的需求决定是否使用 Subscriber Index.


 
EventBus 到这里就算是基本分析完了, 还剩下一些取消注册, 判断是否有注册 等方法. 有兴趣的朋友可以自己研究一下. 如果是从第一篇跟着看到这里的朋友, 看剩下的那些代码, 肯定是非常 Easy 的. 希望大家在看完EventBus 源码分析系列后都能有所收获. 再见!

相关文章

网友评论

    本文标题:这可能是最详细的 EventBus 源码分析05 - Subsc

    本文链接:https://www.haomeiwen.com/subject/tfjqgktx.html