Analysis of Android's data transmission tool and low coupler EventBus

I believe that Activity, Service, Broadcast and other components are not unfamiliar to android application developers. The interaction between them is realized by passing basic data types and custom data types through Intent. If there is a lot of data, it will be very confusing. , especially broadcast storms. Everyone may have learned the observer mode in the basic learning of java, and I believe you also understand it! The observer pattern is to register a listener on an object, and when the object changes, the listener event will be triggered. This blog post provides a detailed explanation of EventBus, which is very useful for data transmission tools and low couplers, and hopes to be beneficial to readers:

1. EventBus concept

EventBus是android提供的一个开源库,方便android应用开发者使用观察者模式。
EvnetBus的下载地址:https://github.com/greenrobot/EventBus.git

2. Steps to use EventBus

1、下载EventBus库:
2、将EventBus.jar放入自己工程的libs目录即可
3、定义一个事件,这个事件一旦被EventBus分发出去就是代表某一件事情发生了,这个事件就是某个观察者关心的事情(不需要继承任何类)
4、定义观察者,然后将该观察者注册到EventBus
5、由EventBus分发事件,告知观察者某一件事情发生了
6、使用完成后从EventBus中反注册观察者。
熟悉观察者模式的朋友肯定对于上面的流程非常熟悉,其实和观察模式基本是一样的。但是也是有区别的。在观察者模式中,所有的观察者都需要实现一个接口,这个接口有一个统一的方法如:

public void onUpdate();
then when an event occurs, an object will call the observer's onUpdate method to notify the observer that something has happened, but this is not required in EventBus, which is implemented in EventBus:
in EventBus There are usually four subscription functions (that is, the method that is called when something happens)
1, onEvent
2, onEventMainThread
3, onEventBackground
4, onEventAsync
These four subscription functions start with onEvent, and their functions are slightly Different, before introducing the difference, first introduce two concepts:
informing the observer that the event occurs through the EventBus.post function, this process is called event publishing, and the observer is informed that the event occurs is called event reception, which is implemented through the following subscription function. of.

onEvent: If onEvent is used as the subscription function, then which thread is the event published, onEvent will run in this thread, that is to say, the thread that publishes the event and the thread that receives the event are in the same thread. When using this method, time-consuming operations cannot be performed in the onEvent method, and event distribution delays may occur if time-consuming operations are performed.
onEventMainThread: If onEventMainThread is used as the subscription function, no matter which thread the event is published in, onEventMainThread will be executed in the UI thread, and the received event will run in the UI thread, which is very useful in Android, because in Android can only follow the new UI in the UI thread, so time-consuming operations cannot be performed in the onEvnetMainThread method.
onEvnetBackground: If onEventBackgrond is used as the subscription function, then if the event is published in the UI thread, then onEventBackground will run in the sub-thread, if the event is originally published in the sub-thread, then the onEventBackground function is directly in the sub-thread in execution.
onEventAsync: Use this function as a subscription function, then no matter which thread the event is published on, a new child thread will be created to execute onEventAsync.

3. Demo example

 demo包含的文件有:类文件-MainActivity.class、SecondActivity.class、FirstEvent.class;布局文件:activity_main.xml、second.xml;还有EventBus的jar包
 1.MainActivity.class如下:
package com.jhsys.cn.eventbusdemo;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;

public class MainActivity extends Activity {

    private Button firstBtn;
    private TextView firstTv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        EventBus.getDefault().register(this);

        firstBtn = (Button) findViewById(R.id.btn_1);
        firstTv = (TextView) findViewById(R.id.tv_1);

        firstBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Intent intent = new Intent();
                intent.setClass(MainActivity.this,SecondActivity.class);
                startActivity(intent);
            }
        });
    }

    @Subscribe
    public void onEventMainThread(FirstEvent event) {

        String msg = "onEventMainThread收到了消息:" + event.getMsg();
        Log.d("harvic", msg);
        firstTv.setText(msg);
        Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }
}

2. The SecondActivity.class file is as follows:

package com.jhsys.cn.eventbusdemo;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

import org.greenrobot.eventbus.EventBus;

/**
 * Created by Administrator on 2016/3/9.
 */
public class SecondActivity extends Activity{

    private Button secondBtn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.second);

        secondBtn = (Button) findViewById(R.id.btn_2);
        secondBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                EventBus.getDefault().post(new FirstEvent("今天下大雨呢!!!"));

            }
        });
    }
}

3. The FirstEvent.class file is as follows:

package com.jhsys.cn.eventbusdemo;

/**
 * Created by Administrator on 2016/3/9.
 */
public class FirstEvent {

    private String mMsg;

    public FirstEvent(String msg) {
        mMsg = msg;
    }
    public String getMsg(){
        return mMsg;
    }
}

4.activity_main.xml file:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity"
    android:gravity="center_horizontal"
    android:orientation="vertical">

    <Button

        android:id="@+id/btn_1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第一个界面  点击"/>

    <TextView
        android:id="@+id/tv_1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""/>

</LinearLayout>

5. The second.xml file is as follows:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:gravity="center"
              android:orientation="vertical">

    <Button
        android:id="@+id/btn_2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第二个界面 点击"/>

</LinearLayout>

6. EventBus jar package
EventBus jar package download address:

Four, EventBus source code analysis

Let's start with the entry of EvnetBus: EventBus.register

public void register(Object subscriber) {  
    register(subscriber, DEFAULT_METHOD_NAME, false, 0);  
}  

In fact, the function register of the same name is called, and the meanings of its four parameters are:
subscriber: is a subscriber to be registered,
methodName: is the default subscription function name of the subscriber, which is actually "onEvent"
sticky: indicates whether it is sticky or not , the default is generally false, unless you call the registerSticky method
priority: indicates the priority of the event, the default is fine,
then let's see what this function does

private synchronized void register(Object subscriber, String methodName, boolean sticky, int priority) {  
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass(),  
                methodName);  
        for (SubscriberMethod subscriberMethod : subscriberMethods) {  
            subscribe(subscriber, subscriberMethod, sticky, priority);  
        }  
}

Find all subscription methods in a subscriber through a findSubscriberMethods method, return a List, enter findSubscriberMethods to see how it is implemented

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass, String eventMethodName) {  
        //通过订阅者类名+"."+"onEvent"创建一个key  
        String key = subscriberClass.getName() + '.' + eventMethodName;  
        List<SubscriberMethod> subscriberMethods;  
        synchronized (methodCache) {  
            //判断是否有缓存,有缓存直接返回缓存  
            subscriberMethods = methodCache.get(key);  
        }  
        //第一次进来subscriberMethods肯定是Null  
        if (subscriberMethods != null) {  
            return subscriberMethods;  
        }  
        subscriberMethods = new ArrayList<SubscriberMethod>();  
        Class<?> clazz = subscriberClass;  
        HashSet<String> eventTypesFound = new HashSet<String>();  
        StringBuilder methodKeyBuilder = new StringBuilder();  
        while (clazz != null) {  
            String name = clazz.getName();  
            //过滤掉系统类  
            if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {  
                // Skip system classes, this just degrades performance  
                break;  
            }  

            // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)  
            //通过反射,获取到订阅者的所有方法  
            Method[] methods = clazz.getMethods();  
            for (Method method : methods) {  
                String methodName = method.getName();  
                //只找以onEvent开头的方法  
                if (methodName.startsWith(eventMethodName)) {  
                    int modifiers = method.getModifiers();  
                    //判断订阅者是否是public的,并且是否有修饰符,看来订阅者只能是public的,并且不能被final,static等修饰  
                    if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {  
                        //获得订阅函数的参数  
                        Class<?>[] parameterTypes = method.getParameterTypes();  
                        //看了参数的个数只能是1个  
                        if (parameterTypes.length == 1) {  
                            //获取onEvent后面的部分  
                            String modifierString = methodName.substring(eventMethodName.length());  
                            ThreadMode threadMode;  
                            if (modifierString.length() == 0) {  
                                //订阅函数为onEvnet  
                                //记录线程模型为PostThread,意义就是发布事件和接收事件在同一个线程执行,详细可以参考我对于四个订阅函数不同点分析  
                                threadMode = ThreadMode.PostThread;  
                            } else if (modifierString.equals("MainThread")) {  
                                //对应onEventMainThread  
                                threadMode = ThreadMode.MainThread;  
                            } else if (modifierString.equals("BackgroundThread")) {  
                                //对应onEventBackgrondThread  
                                threadMode = ThreadMode.BackgroundThread;  
                            } else if (modifierString.equals("Async")) {  
                                //对应onEventAsync  
                                threadMode = ThreadMode.Async;  
                            } else {  
                                if (skipMethodVerificationForClasses.containsKey(clazz)) {  
                                    continue;  
                                } else {  
                                    throw new EventBusException("Illegal onEvent method, check for typos: " + method);  
                                }  
                            }  
                            //获取参数类型,其实就是接收事件的类型  
                            Class<?> eventType = parameterTypes[0];  
                            methodKeyBuilder.setLength(0);  
                            methodKeyBuilder.append(methodName);  
                            methodKeyBuilder.append('>').append(eventType.getName());  
                            String methodKey = methodKeyBuilder.toString();  
                            if (eventTypesFound.add(methodKey)) {  
                                // Only add if not already found in a sub class  
                                //封装一个订阅方法对象,这个对象包含Method对象,threadMode对象,eventType对象  
                                subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));  
                            }  
                        }  
                    } else if (!skipMethodVerificationForClasses.containsKey(clazz)) {  
                        Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."  
                                + methodName);  
                    }  
                }  
            }  
            //看了还会遍历父类的订阅函数  
            clazz = clazz.getSuperclass();  
        }  
        //最后加入缓存,第二次使用直接从缓存拿  
        if (subscriberMethods.isEmpty()) {  
            throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "  
                    + eventMethodName);  
        } else {  
            synchronized (methodCache) {  
                methodCache.put(key, subscriberMethods);  
            }  
            return subscriberMethods;  
        }  
    } 

The explanation of this method is in the comments, so I won't repeat it here. Here we find all the subscription methods of a subscriber.
Let's go back to the register method:

for (SubscriberMethod subscriberMethod : subscriberMethods) {  
           subscribe(subscriber, subscriberMethod, sticky, priority);  
       } 

For each subscription method, call the subscribe method on it, and enter the method to see what has been done

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {  
        subscribed = true;  
        //从订阅方法中拿到订阅事件的类型  
        Class<?> eventType = subscriberMethod.eventType;  
        //通过订阅事件类型,找到所有的订阅(Subscription),订阅中包含了订阅者,订阅方法  
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);  
        //创建一个新的订阅  
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);  
        //将新建的订阅加入到这个事件类型对应的所有订阅列表  
        if (subscriptions == null) {  
            //如果该事件目前没有订阅列表,那么创建并加入该订阅  
            subscriptions = new CopyOnWriteArrayList<Subscription>();  
            subscriptionsByEventType.put(eventType, subscriptions);  
        } else {  
            //如果有订阅列表,检查是否已经加入过  
            for (Subscription subscription : subscriptions) {  
                if (subscription.equals(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 || newSubscription.priority > subscriptions.get(i).priority) {  
                subscriptions.add(i, newSubscription);  
                break;  
            }  
        }  
        //将这个订阅事件加入到订阅者的订阅事件列表中  
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);  
        if (subscribedEvents == null) {  
            subscribedEvents = new ArrayList<Class<?>>();  
            typesBySubscriber.put(subscriber, subscribedEvents);  
        }  
        subscribedEvents.add(eventType);  
        //这个是对粘性事件的,暂时不讨论  
        if (sticky) {  
            Object stickyEvent;  
            synchronized (stickyEvents) {  
                stickyEvent = stickyEvents.get(eventType);  
            }  
            if (stickyEvent != null) {  
                postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());  
            }  
        }  
    }

Well, the analysis of the register method is almost finished here. The general process is like this. Let's summarize:
1. Find all the subscription methods in the registered person.
2. Traverse the subscription methods in turn, find the subscription list corresponding to the eventType in EventBus, and then create a new subscription according to the current subscriber and subscription method to add to the subscription list
3. Find the event list subscribed by the subscriber in EvnetBus, and add the eventType to this event list.

So for any subscriber, we can find a list of its subscription event types, and through this subscription event type, we can find the subscription function in the subscriber.

After analyzing the register, analyze the post. After this analysis, the principle of EventBus is almost finished...

public void post(Object event) {  
        //这个EventBus中只有一个,差不多是个单例吧,具体不用细究  
        PostingThreadState postingState = currentPostingThreadState.get();  
        List<Object> eventQueue = postingState.eventQueue;  
        //将事件放入队列  
        eventQueue.add(event);  

        if (postingState.isPosting) {  
            return;  
        } else {  
            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;  
            }  
        }  
    } 

There is no specific logic in post, its function is mainly completed by calling postSingleEvent, enter this function to see

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {  
       Class<? extends Object> eventClass = event.getClass();  
    //找到eventClass对应的事件,包含父类对应的事件和接口对应的事件  
       List<Class<?>> eventTypes = findEventTypes(eventClass);  
       boolean subscriptionFound = false;  
       int countTypes = eventTypes.size();  
       for (int h = 0; h < countTypes; h++) {  
           Class<?> clazz = eventTypes.get(h);  
           CopyOnWriteArrayList<Subscription> subscriptions;  
           synchronized (this) {  
            //找到订阅事件对应的订阅,这个是通过register加入的(还记得吗....)  
               subscriptions = subscriptionsByEventType.get(clazz);  
           }  
           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;  
                   }  
               }  
               subscriptionFound = true;  
           }  
       }  
    //如果没有订阅发现,那么会Post一个NoSubscriberEvent事件  
       if (!subscriptionFound) {  
           Log.d(TAG, "No subscribers registered for event " + eventClass);  
           if (eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) {  
               post(new NoSubscriberEvent(this, event));  
           }  
       }  
   } 

This method has a core method postToSubscription method, enter it and take a look

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {  
        //第一个参数就是传入的订阅,第二个参数就是对于的分发事件,第三个参数非常关键:是否在主线程  
        switch (subscription.subscriberMethod.threadMode) {  
        //这个threadMode是怎么传入的,仔细想想?是不是根据onEvent,onEventMainThread,onEventBackground,onEventAsync决定的?  
        case PostThread:  
            //直接在本线程中调用订阅函数  
            invokeSubscriber(subscription, event);  
            break;  
        case MainThread:  
            if (isMainThread) {  
                //如果直接在主线程,那么直接在本现场中调用订阅函数  
                invokeSubscriber(subscription, event);  
            } else {  
                //如果不在主线程,那么通过handler实现在主线程中执行,具体我就不跟踪了  
                mainThreadPoster.enqueue(subscription, event);  
            }  
            break;  
        case BackgroundThread:  
            if (isMainThread) {  
                //如果主线程,创建一个runnable丢入线程池中  
                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);  
        }  
    }

The source code Demo uses the download address:

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325850757&siteId=291194637