| 注册
请输入搜索内容

热门搜索

Java Linux MySQL PHP JavaScript Hibernate jQuery Nginx
woruiz
8年前发布

android 消息传递机制进阶EventBus的深入探究

   <p>以前,对于activity和fragment之间的通信可以使用接口的方式,定义起来比较麻烦费事,偶然间发现可以使用EventBus,发现很好用,查看了一下官方说明:EventBus是针一款对Android的发布/订阅事件总线。它可以让我们很轻松的实现在Android各个组件之间传递消息,并且代码的可读性更好,耦合度更低。但是在用的过程中总会出现一些问题,下面就将出现的问题详细记录一下,顺便具体学习EventBus(GreenRobot)这个第三方开源库,了解它内部的实现原理,以至于出了问题可以快速定位修复。</p>    <p>官网: <a href="/misc/goto?guid=4959747666429778699" rel="nofollow,noindex">http://greenrobot.org/eventbus/documentation/</a></p>    <p>github: <a href="/misc/goto?guid=4959747666529992484" rel="nofollow,noindex">https://github.com/greenrobot/EventBus</a></p>    <p>以下使用都是基于EventBus3.0。</p>    <h3>EventBus3.0使用</h3>    <p>对于EventBus的原理呢,可以参照一下官网的这张图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/586ffc91de6a78212c45c7dfee7e3e91.png"></p>    <p>具体的使用方法可以看官网,很简单,简单罗列一下:</p>    <p>Step 1: Add Gradle</p>    <pre>  <code class="language-java">compile 'org.greenrobot:eventbus:3.0.0'</code></pre>    <p>Step 2: Define events</p>    <pre>  <code class="language-java">public class MessageEvent {        public final String message;        public MessageEvent(String message) {          this.message = message;      }  }</code></pre>    <p>Step 3: Prepare subscribers</p>    <p>Subscribers implement event handling methods (also called “subscriber methods”) that will be called when an event is posted. These are defined with the @Subscribe annotation.Note that with EventBus 3 the method name can be chosen freely (no naming conventions like in EventBus 2).</p>    <pre>  <code class="language-java">// This method will be called when a MessageEvent is posted (in the UI thread for Toast)  @Subscribe(threadMode = ThreadMode.MAIN)  public void onMessageEvent(MessageEvent event) {      Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();  }</code></pre>    <p>Subscribers also need to register themselves to and unregister from the bus. Only while subscribers are registered, they will receive events. In Android, in activities and fragments you should usually register according to their life cycle:</p>    <pre>  <code class="language-java">@Override  public void onStart() {      super.onStart();      EventBus.getDefault().register(this);  }    @Override  public void onStop() {      EventBus.getDefault().unregister(this);      super.onStop();  }</code></pre>    <p>Step 4: Post events</p>    <pre>  <code class="language-java">EventBus.getDefault().post(new MessageEvent("Hello everyone!"));</code></pre>    <p>以上便是官网给出的简单使用步骤,也不是很难,所以就不需要翻译了。接下来我们针对使用过程中出现的问题来进行一步一步的深入探究。</p>    <h3>踩坑</h3>    <p>我们先来使用一个简单的例子来总结。这个例子主要有三个activity,MainActivity、SecondActivity、ThirdActivity以及一个MessageEvent对象。我们在MainActivity、SecondActivity中分别注册了MessageEvent事件,在ThirdActivity中post MessageEvent事件,这样我们在MainActivity、SecondActivity中应该都能接受到该事件。下面是具体的代码。</p>    <p>第一个MainActivity</p>    <pre>  <code class="language-java">public class MainActivity extends AppCompatActivity {      @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_main);            EventBus.getDefault().register(this);          Button btn = (Button) findViewById(R.id.button2);          btn.setOnClickListener(new View.OnClickListener() {              @Override              public void onClick(View v) {                  startActivity(new Intent(MainActivity.this, SecondActivity.class));              }          });      }        //接收事件      @Subscribe(threadMode = ThreadMode.MAIN)      public void fresh(MessageEvent messageEvent) {          M2Log.d("MessageEvent -----------------> MainActivity");      }          @Override      protected void onDestroy() {          super.onDestroy();          M2Log.d("MainActivity -----------------> onDestroy");          EventBus.getDefault().unregister(this);      }  }</code></pre>    <p>第二个SecondActivity</p>    <pre>  <code class="language-java">public class SecondActivity extends AppCompatActivity {        @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_main2);          EventBus.getDefault().register(this);          Button btn = (Button) findViewById(R.id.btn2);          btn.setOnClickListener(new View.OnClickListener() {              @Override              public void onClick(View v) {                  startActivity(new Intent(SecondActivity.this, ThirdActivity.class));              }          });      }        //接收事件      @Subscribe(threadMode = ThreadMode.MAIN)      public void fresh(MessageEvent messageEvent) {          M2Log.d("MessageEvent -----------------> SecondActivity");      }        @Override      protected void onDestroy() {          super.onDestroy();          M2Log.d("SecondActivity -----------------> onDestroy");          EventBus.getDefault().unregister(this);      }  }</code></pre>    <p>第三个ThirdActivity</p>    <pre>  <code class="language-java">public class ThirdActivity extends AppCompatActivity {        @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_main3);          Button btn = (Button) findViewById(R.id.btn3);          btn.setOnClickListener(new View.OnClickListener() {              @Override              public void onClick(View v) {                  //发送消息                  EventBus.getDefault().post(new MessageEvent(""));                  finish();              }          });      }  }</code></pre>    <p>打印输出结果</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ae83dc1440665856f3f00fecee342c39.png"></p>    <p>很显然,MainActivity和SecondActivity都接收到了MessageEvent事件。</p>    <p>细心的会发现,我们</p>    <p>EventBus.getDefault().register(this);</p>    <p>EventBus.getDefault().unregister(this);</p>    <p>注册生命周期是放在onCreate()和onDestroy()中的,如果我们按照官网上来,放在onStart()和onStop()中,你就会发现,我们接收不到MessageEvent事件,可以验证一下</p>    <pre>  <code class="language-java">@Override      protected void onStart() {          super.onStart();          M2Log.d("SecondActivity -----------------> onStart");          EventBus.getDefault().register(this);      }        @Override      protected void onStop() {          super.onStop();          M2Log.d("SecondActivity -----------------> onStop");          EventBus.getDefault().unregister(this);      }</code></pre>    <p>结果是什么都不会打印,所以我们一般会将注册生命周期放到onCreate()和onDestroy()中去。</p>    <p>我们在开发过程中,你会发现有的时候会出现问题:</p>    <p>1、没有注册该事件</p>    <p><img src="https://simg.open-open.com/show/9e5d10181ac10b51f71faa7163e5897c.png"></p>    <p>出现这种情况,大多数是没有注册该事件,什么意思呢?就是下面的类似代码没有写。</p>    <pre>  <code class="language-java">@Subscribe(threadMode = ThreadMode.MAIN)   public void fresh(MessageEvent messageEvent) {          M2Log.d("MessageEvent -----------------> MainActivity");   }</code></pre>    <p>有的人写了类似的注册代码,但还是会报这个错误,那就涉及到注册的生命周期了。</p>    <pre>  <code class="language-java">@Override  public void onStart() {      super.onStart();      EventBus.getDefault().register(this);  }    @Override  public void onStop() {      EventBus.getDefault().unregister(this);      super.onStop();  }</code></pre>    <p>2、多次注册</p>    <p><img src="https://simg.open-open.com/show/6621b1aff1eb5b2c89906df3d08efec9.png"></p>    <p>这是我们在生命周期中注册该事件时多次注册造成的。解决方法很简单,可以判断一下</p>    <pre>  <code class="language-java">//没有注册时再进行注册操作          if (!EventBus.getDefault().isRegistered(this)){              EventBus.getDefault().register(this);          }</code></pre>    <h3>粘性事件Sticky Events</h3>    <p>粘性事件类似于粘性广播,就是一次注册永久使用。</p>    <p>如何使用呢?类似于前面的,只不过加了一个sticky = true,发送时采用postSticky而已</p>    <pre>  <code class="language-java">//发布事件    EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));</code></pre>    <p>在接收的时候添加一个sticky = true即可。</p>    <pre>  <code class="language-java">//注册接收    @Subscribe(threadMode = ThreadMode.MAIN,sticky = true)      public void fresh(MessageEvent messageEvent) {          M2Log.d("MessageEvent -----------------> SecondActivity");      }        @Override      protected void onStart() {          super.onStart();          M2Log.d("SecondActivity -----------------> onStart");          EventBus.getDefault().register(this);      }        @Override      protected void onStop() {          super.onStop();          M2Log.d("SecondActivity -----------------> onStop");          EventBus.getDefault().unregister(this);      }</code></pre>    <p>我们前面出现过一个问题,那就是我们在onStart和onStop中注册,接收不到EventMessage,通过粘性事件,就可以解决这个问题。不过当你使用粘性事件时你会发现,每次进入注册该事件的activity中都会主动接收到该事件。</p>    <p>下面是我发送了一个粘性事件,我们在MainActivity 和 SecondActivity中会接收到该事件,我们退出APP后,再次进入,则又会接收到该事件。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/17dca140ce54ce7b2fbf5f775801abec.png"></p>    <p>清除粘性事件</p>    <pre>  <code class="language-java">MessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MessageEvent.class);  // Better check that an event was actually posted before  if(stickyEvent != null) {     // "Consume" the sticky event     EventBus.getDefault().removeStickyEvent(stickyEvent);     // Now do something with it  }</code></pre>    <pre>  <code class="language-java">/**   * threadMode    * 表示方法在什么线程执行 (Android更新UI只能在主线程, 所以如果需要操作UI, 需要设置ThreadMode.MainThread)   *    * sticky        * 表示是否是一个粘性事件 (如果你使用postSticky发送一个事件,那么需要设置为true才能接受到事件)   *    * priority      * 优先级 (如果有多个对象同时订阅了相同的事件, 那么优先级越高,会优先被调用.)   * */  @Subscribe(threadMode = ThreadMode.MainThread, sticky = true, priority = 100)  public void onEvent(MsgEvent event){  }</code></pre>    <p>上面便是EventBus3.0的常规用法,我们在知道了常规用法后还不行,必须深入了解一下它的内部实现原理,否则到时候出了问题后不知道该如何解决,要知其然而之所以然。下面我们便来分析一下它的源码。</p>    <h3>源码解析(EventBus3.0)</h3>    <p>源码解析部分主要从register、post、以及unregisger这三部分进行分析。</p>    <p>register分析</p>    <p>我们首先从注册入手,先分析</p>    <pre>  <code class="language-java">EventBus.getDefault()</code></pre>    <p>进入源码:</p>    <pre>  <code class="language-java">static volatile EventBus defaultInstance;    /** 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;      }</code></pre>    <p>EventBus是单例模式存在的,使用了双重判断的方式,防止并发的问题,还能极大的提高效率。接着进入register(this)进行分析</p>    <pre>  <code class="language-java">//**EventBus.class ---> register**     /**       * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they       * are no longer interested in receiving events.       * <p/>       * 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();            //获取订阅者所有的订阅方法以@Subscribe为注解的一些public方法          List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);            //依次注册这些订阅方法          synchronized (this) {              for (SubscriberMethod subscriberMethod : subscriberMethods) {                  //对订阅方法进行注册                  subscribe(subscriber, subscriberMethod);              }          }      }</code></pre>    <p>其中有获取订阅方法的代码</p>    <pre>  <code class="language-java">List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);</code></pre>    <p>我们进入,分析一下,如何获取订阅方法。首先来看一下订阅方法的类</p>    <pre>  <code class="language-java">//**SubscriberMethod.class**    /** Used internally by EventBus and generated subscriber indexes. */  public class SubscriberMethod {      final Method method; //方法      final ThreadMode threadMode; //执行线程      final Class<?> eventType; //接收的事件类型      final int priority; //优先级      final boolean sticky; //粘性事件      /** Used for efficient comparison */      String methodString;        //...省略部分代码        }</code></pre>    <p>SubscriberMethod是一个订阅方法的实体类,里面存储了订阅方法的一些基本信息,订阅方法就是在类中以 <strong>@Subscribe</strong> 为注解的一些 <strong>public</strong> 方法, 注意是public方法否则会报错,为什么是public方法我们下面会分析,给出原因 ,然后进入subscriberMethodFinder.findSubscriberMethods(subscriberClass),该代码的作用主要是获取当前类中所有的订阅方法。我们来看看是如何获取一个订阅者所有的订阅方法的:</p>    <pre>  <code class="language-java">//**SubscriberMethodFinder.class**    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {            //从缓存中获取订阅方法          List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);            if (subscriberMethods != null) {              return subscriberMethods;          }            //是否忽略注解器生成的MyEventBusIndex类          if (ignoreGeneratedIndex) {              //利用反射来获取订阅类中的订阅方法信息              subscriberMethods = findUsingReflection(subscriberClass);          } else {              //从注解器生成的MyEventBusIndex类中获得订阅类的订阅方法信息              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;          }      }</code></pre>    <p>对于获取我们注册的订阅方法,首先就是通过缓存来获取,如果没有的话则通过以下两种方式进行获取:</p>    <ol>     <li>EventBusAnnotationProcessor注解生成器在编译期通过读取@Subscribe()注解并解析,处理其中所包含的信息,然后生成java类来保存所有订阅者关于订阅的信息。</li>     <li>运行时使用反射来获得这些订阅者的信息</li>    </ol>    <p>对于第一种方法没什么好说的,我们来分析一下通过反射来获取这些订阅方法的方式,接下来分析通过反射获取当前类中的订阅方法</p>    <pre>  <code class="language-java">//**SubscriberMethodFinder.class**    private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {            //FindState其实就是一个里面保存了订阅者和订阅方法信息的一个实体类,包括订阅类中所有订阅的事件类型和所有的订阅方法等。          FindState findState = prepareFindState();          findState.initForSubscriber(subscriberClass);          while (findState.clazz != null) {                //获取订阅方法              findUsingReflectionInSingleClass(findState);              findState.moveToSuperclass();          }          return getMethodsAndRelease(findState);      }</code></pre>    <p>代码不是太多,里面涉及到一个类FindState,我们来看下,这是什么东西,</p>    <pre>  <code class="language-java">static class FindState {          //订阅方法          final List<SubscriberMethod> subscriberMethods = new ArrayList<>();          //以event为key,以method为value          final Map<Class, Object> anyMethodByEventType = new HashMap<>();          //以method的名字生成一个methodKey为key,该method的类(订阅者)为value          final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();          final StringBuilder methodKeyBuilder = new StringBuilder(128);            Class<?> subscriberClass;          Class<?> clazz;          boolean skipSuperClasses;          SubscriberInfo subscriberInfo;          //...省略部分代码  }</code></pre>    <p>这个FindState其实就是一个里面保存了订阅者和订阅方法信息的一个实体类,包括订阅类中所有订阅的事件类型和所有的订阅方法等。我们接着分析下面的代码。</p>    <pre>  <code class="language-java">findUsingReflectionInSingleClass(findState)</code></pre>    <p>这行代码便是获取订阅方法列表的重要代码,我们进入查看一下:</p>    <pre>  <code class="language-java">//**SubscriberMethodFinder.class**    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) {                //获取方法的访问修饰权              int modifiers = method.getModifiers();                //订阅方法必须是must be public, non-static, and non-abstract                      //获取订阅方法参数类型                  Class<?>[] parameterTypes = method.getParameterTypes();                    //注解方法必须只有一个参数                  if (parameterTypes.length == 1) {                        //获取订阅方法的注解                      Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);                        //该订阅方法包含Subscribe注解                      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");              }          }      }</code></pre>    <p>可以看到,首先会得到订阅类的class对象并通过反射获取订阅类中的所有方法信息,然后通过筛选获取到订阅方法集合。 这里面就解释了为什么要以@Subscribe为注解的方法,且必须是public类型,方法参数只有一个的原因。</p>    <pre>  <code class="language-java">//**Subscribe.java**    @Documented  @Retention(RetentionPolicy.RUNTIME)  @Target({ElementType.METHOD})  public @interface Subscribe {      ThreadMode threadMode() default ThreadMode.POSTING;        /**       * If true, delivers the most recent sticky event (posted with       * {@link EventBus#postSticky(Object)}) to this subscriber (if event available).       */      boolean sticky() default false;        /** Subscriber priority to influence the order of event delivery.       * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before       * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of       * delivery among subscribers with different {@link ThreadMode}s! */      int priority() default 0;  }</code></pre>    <p>注解,分为三种参数,</p>    <p>ThreadMode,方法执行的线程,POSTING(默认值)、MAIN、BACKGROUND、ASYNC</p>    <p>sticky,粘性时间,默认值false</p>    <p>priority,优先级,默认值0</p>    <p>该方法流程是:</p>    <ol>     <li>拿到当前 class 的所有方法</li>     <li>过滤掉不是 public 和是 abstract、static、bridge、synthetic 的方法 过滤出方法参数只有一个的方法</li>     <li>过滤出被Subscribe注解修饰的方法</li>     <li>将 method 方法和 event 事件添加到 findState 中</li>     <li>将 EventBus 关心的 method 方法、event 事件、threadMode、priority、sticky 封装成SubscriberMethod 对象添加到 findState.subscriberMethods 列表中</li>    </ol>    <p>通过上面几步,我们就可以获得了所订阅的方法,然后分别进行注册这些订阅方法。通过下面的代码来执行:</p>    <pre>  <code class="language-java">//参数:1订阅者 2订阅方法  subscribe(subscriber, subscriberMethod);</code></pre>    <p>接着分析这个注册方法。</p>    <pre>  <code class="language-java">//**EventBus.java**  // Must be called in synchronized block      private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {            //订阅方法的参数类型,也是事件类型          Class<?> eventType = subscriberMethod.eventType;            //订阅方法描述,实体类(当前类中的订阅方法)          Subscription newSubscription = new Subscription(subscriber, subscriberMethod);            //获取当前类中的所有订阅方法          CopyOnWriteArrayList<Subscription> 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();          for (int i = 0; i <= size; i++) {                //根据优先级,将订阅者插入到指定的位置              if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {                  subscriptions.add(i, newSubscription);                  break;              }          }            //获取订阅者所有订阅的事件类型          List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);          if (subscribedEvents == null) {              subscribedEvents = new ArrayList<>();              typesBySubscriber.put(subscriber, subscribedEvents);          }          //将该事件类型添加到typesBySubscriber中          subscribedEvents.add(eventType);            //如果接收sticky事件,立即分发sticky事件          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<Class>).                  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);              }          }      }</code></pre>    <p>这里面涉及到一些对象,我们分别注释一下:</p>    <pre>  <code class="language-java">//Subscription.java    //订阅者信息   final class Subscription {  final Object subscriber;//订阅者  final SubscriberMethod subscriberMethod;//订阅方法  }      //subscriptionsByEventType  key订阅方法类型 values 所有订阅了该类型的订阅者集合  Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;    //typesBySubscriber  key订阅者 values订阅事件集合  Map<Object, List<Class<?>>> typesBySubscriber;</code></pre>    <p>了解了这几个对象,上面的代码就很容易看懂了,</p>    <ol>     <li>首先获取订阅方法的参数类型即订阅事件类型</li>     <li>根据订阅事件类型获取该事件类型的所有订阅者</li>     <li>将该订阅者添加到该事件类型的订阅者集合中即:subscriptionsByEventType</li>     <li>获取订阅者所有的订阅事件类型</li>     <li>将该事件类型添加到该订阅者的订阅事件类型集中即:typesBySubscriber</li>    </ol>    <p>事件post分析</p>    <p>分析了注册事件后,我们来分析一下分发事件post的流程,首先通过</p>    <pre>  <code class="language-java">EventBus.getDefault().post(new MessageEvent(""));</code></pre>    <p>这行代码进行事件消息的分发,我们进入到post中详细了解一下这个流程。</p>    <pre>  <code class="language-java">/** Posts the given event to the event bus. */      public void post(Object event) {          PostingThreadState postingState = currentPostingThreadState.get();          List<Object> eventQueue = postingState.eventQueue;          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 {                  //分发事件                  while (!eventQueue.isEmpty()) {                      postSingleEvent(eventQueue.remove(0), postingState);                  }              } finally {                  postingState.isPosting = false;                  postingState.isMainThread = false;              }          }      }</code></pre>    <p>代码虽然不长,但是不大好理解,这里面多了一些不常见的对象,我们来看下,首先对于第一行代码:</p>    <pre>  <code class="language-java">PostingThreadState postingState = currentPostingThreadState.get();</code></pre>    <p>这里面的PostingThreadState是什么意思呢?</p>    <pre>  <code class="language-java">/** For ThreadLocal, much faster to set (and get multiple values). */      final static class PostingThreadState {          //当前线程的事件队列          final List<Object> eventQueue = new ArrayList<Object>();          //是否有事件正在分发          boolean isPosting;          //post的线程是否是主线程          boolean isMainThread;          Subscription subscription;          Object event;          boolean canceled;      }</code></pre>    <p>PostingThreadState中包含了当前线程的事件队列,就是当前线程所有分发的事件都保存在eventQueue事件队列中以及订阅者订阅事件等信息,有了这些信息我们就可以从事件队列中取出事件分发给对应的订阅者。</p>    <p>我们接着分析,对于这个当前线程的事件队列,我们是通过 currentPostingThreadState.get(); 来得到的,对于这个currentPostingThreadState又是什么呢?</p>    <pre>  <code class="language-java">private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {          @Override          protected PostingThreadState initialValue() {              return new PostingThreadState();          }      };</code></pre>    <p>ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,而这段数据是不会与其他线程共享的。可以看出currentPostingThreadState的实现是一个包含了PostingThreadState的ThreadLocal对象,这样可以保证取到的都是自己线程对应的数据。</p>    <p>接着就通过 postSingleEvent(eventQueue.remove(0), postingState); 来对事件进行分发。</p>    <pre>  <code class="language-java">private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {          Class<?> eventClass = event.getClass();          boolean subscriptionFound = false;          if (eventInheritance) {              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);          }          if (!subscriptionFound) {              if (logNoSubscriberMessages) {                  Log.d(TAG, "No subscribers registered for event " + eventClass);              }              if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&                      eventClass != SubscriberExceptionEvent.class) {                  post(new NoSubscriberEvent(this, event));              }          }      }</code></pre>    <p>事件的分发最后还要通过 postSingleEventForEventType 它来执行,</p>    <pre>  <code class="language-java">private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {          CopyOnWriteArrayList<Subscription> subscriptions;          synchronized (this) {              //根据事件类型获取所有的订阅者              subscriptions = subscriptionsByEventType.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;      }      //将事件分发给对应的订阅者   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);          }      }</code></pre>    <p>代码比较简单,它们最终是通过发射调用来将事件分发给对应的订阅者的:</p>    <pre>  <code class="language-java">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);          }      }</code></pre>    <p>以上便是事件的分发过程,我们总结概括一下:</p>    <ol>     <li>首先获取当前线程的PostingThreadState对象从而获取到当前线程的事件队列</li>     <li>通过事件类型获取到所有订阅者集合</li>     <li>通过反射执行订阅者中的订阅方法</li>    </ol>    <p>unregister分析</p>    <p>最后我们来分析一下取消订阅的方法:</p>    <pre>  <code class="language-java">/** Unregisters the given subscriber from all event classes. */      public synchronized void unregister(Object subscriber) {         //获取订阅者的所有订阅的事件类型          List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);          if (subscribedTypes != null) {              for (Class<?> eventType : subscribedTypes) {              //从事件类型的订阅者集合中移除订阅者                  unsubscribeByEventType(subscriber, eventType);              }              typesBySubscriber.remove(subscriber);          } else {              Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());          }      }</code></pre>    <pre>  <code class="language-java">/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */      private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {       //获取事件类型的所有订阅者          List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);          //遍历订阅者集合,将解除的订阅者移除          if (subscriptions != null) {              int size = subscriptions.size();              for (int i = 0; i < size; i++) {                  Subscription subscription = subscriptions.get(i);                  if (subscription.subscriber == subscriber) {                      subscription.active = false;                      subscriptions.remove(i);                      i--;                      size--;                  }              }          }      }</code></pre>    <p>代码很简单,最后总结一下取消订阅的流程。</p>    <p>总结一下:</p>    <ol>     <li>首先获取订阅者的所有订阅事件</li>     <li>遍历订阅事件</li>     <li>根据订阅事件获取所有的订阅了该事件的订阅者集合</li>     <li>将该订阅者移除</li>     <li>将步骤1中的集合中的订阅者移除</li>    </ol>    <p>以上便是EventBus所有的工作流程,我们来简单说明一下:</p>    <p>register</p>    <p>1、首先用register()方法注册一个订阅者</p>    <p>2、获取该订阅者的所有订阅的方法</p>    <p>3、根据该订阅者的所有订阅的事件类型,将订阅者存入到每个以 事件类型为key 以所有订阅者为values的map集合中</p>    <p>4、然后将订阅事件添加到以订阅者为key 以订阅者所有订阅事件为values的map集合中</p>    <p>4.1、如果是订阅了粘滞事件的订阅者,从粘滞事件缓存区获取之前发送过的粘滞事件,响应这些粘滞事件。</p>    <p>post</p>    <p>1、首先获取当前线程的事件队列</p>    <p>2、将要发送的事件添加到事件队列中</p>    <p>3、根据发送事件类型获取所有的订阅者</p>    <p>4、根据响应方法的执行模式,在相应线程通过反射执行订阅者的订阅方法</p>    <p>unregister</p>    <p>1、首先通过unregister方法拿到要取消的订阅者</p>    <p>2、得到该订阅者的所有订阅事件类型</p>    <p>3、遍历事件类型,根据每个事件类型获取到所有的订阅者集合,并从集合中删除该订阅者</p>    <p>4、将订阅者从步骤2的集合中移除</p>    <h3>参考</h3>    <p><a href="/misc/goto?guid=4959747666623004610" rel="nofollow,noindex">1、 Android EventBus源码解析 带你深入理解EventBus</a></p>    <p><a href="/misc/goto?guid=4959747666720207326" rel="nofollow,noindex">2、 EventBus 源码解析</a></p>    <p><a href="/misc/goto?guid=4959747666807595593" rel="nofollow,noindex">3、http://www.cnblogs.com/all88/archive/2016/03/30/5338412.html</a></p>    <p><a href="/misc/goto?guid=4959747666902010413" rel="nofollow,noindex">4、http://www.jianshu.com/p/f057c460c77e</a></p>    <p> </p>    <p>来自:https://juejin.im/post/58f5c86a8d6d810057c12975</p>    <p> </p>    
 本文由用户 woruiz 自行上传分享,仅供网友学习交流。所有权归原作者,若您的权利被侵害,请联系管理员。
 转载本站原创文章,请注明出处,并保留原始链接、图片水印。
 本站是一个以用户分享为主的开源技术平台,欢迎各类分享!
 本文地址:https://www.open-open.com/lib/view/open1492570908493.html
EventBus Android开发 移动开发