面試題之---EventBus源碼解析

2021-12-23 Android編程精選
(一)介紹1,EvenetBus是一種發布-訂閱事件總線.

代碼簡潔,開銷小,並很好的實現了發送者和接收者的解耦.(是一種觀察者模式)

2,三要素:3,通常情況下安卓下數據的傳遞有下面幾種方法:

3.1.通過intent傳遞,包括顯式意圖和隱式意圖,廣播(Broadcast)和服務都能通過Intent傳遞
傳遞的數據類型包括8大基本數據類型    實現Parcelable或Serializable接口的類型   以及集合數組類型

3.2.靜態變量傳遞  在工具類下 聲明一個Object類型的靜態變量   在A中將要傳遞的值,在B中通過這個靜態變量取出來

3.3.通過handle在不同的線程中傳遞Object類型的數據

3.4.通過構造方法傳遞Object類型的數據

3.5.通過SharedPreferences傳遞八大基本數據類型

3.6.通過ContentProvider在進程間共享數據

3.7.通過aidl在進程進程傳遞數據

3.8.通過流(本地文件)傳遞八大基本數據類型和實現Serializable接口的數據類型

3.9.通過基類中的屬性或者方法
屬性: 基類公有屬性  在某個子類中賦值   其他子類中都能使用
方法: 子類調用父類的某個方法給父類某個屬性賦值  另外一個子類通過父類的另一個公有方法獲取這個值(這個方法把值返回)

(二)基本使用先訂閱,後發布

    compile 'org.greenrobot:eventbus:3.1.1'

public class MessageEvent {

    private String message;

    public MessageEvent(String message){
        this.message = message;
    }

    public String getMessage(){
        return message;
    }
}

      
      EventBus.getDefault().register(this);

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

    
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(MessageEvent messageEvent){
        Log.e("date","receive it");
        Toast.makeText(ViewPageStep1Activity.this, messageEvent.getMessage(), Toast.LENGTH_SHORT).show();
    }

EventBus.getDefault().post(new MessageEvent("從fragment將數據傳遞到activity22222222"));

先發布,再訂閱,黏性事件

     
      EventBus.getDefault().register(this);

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

    
    @Subscribe(threadMode = ThreadMode.MAIN,stick = true)
    public void onEvent(MessageEvent messageEvent){
        Log.e("date","receive it");
        Toast.makeText(ViewPageStep1Activity.this, messageEvent.getMessage(), Toast.LENGTH_SHORT).show();
    }

EventBus.getDefault().postSticky(new MessageEvent("從fragment將數據傳遞到activity22222222"));

(三)四個訂閱方法onEvent:

如果使用onEvent作為訂閱函數,那麼該事件在哪個線程發布出來的,onEvent就會在這個線程中運行,也就是說發布事件和接收事件線程在同一個線程。使用這個方法時,在onEvent方法中不能執行耗時操作,如果執行耗時操作容易導致事件分發延遲。

onEventMainThread:

如果使用onEventMainThread作為訂閱函數,那麼不論事件是在哪個線程中發布出來的,onEventMainThread都會在UI線程中執行,接收事件就會在UI線程中運行,這個在Android中是非常有用的,因為在Android中只能在UI線程中跟新UI,所以在onEvnetMainThread方法中是不能執行耗時操作的。

onEventBackground:

如果使用onEventBackgrond作為訂閱函數,那麼如果事件是在UI線程中發布出來的,那麼onEventBackground就會在子線程中運行,如果事件本來就是子線程中發布出來的,那麼onEventBackground函數直接在該子線程中執行。

onEventAsync:

使用這個函數作為訂閱函數,那麼無論事件在哪個線程發布,都會創建新的子線程在執行onEventAsync。

(四)源碼解析

可以看到,發布者(Publisher)使用post()方法將Event發送到Event Bus,而後Event Bus自動將Event發送到多個訂閱者(Subcriber)。這裡需要注意兩個地方:
(1)一個發布者可以對應多個訂閱者。
(2)3.0以前訂閱者的訂閱方法為onEvent()、onEventMainThread()、onEventBackgroundThread()和onEventAsync()。在Event Bus3.0之後統一採用註解@Subscribe的形式,具體實現方式見下文。

   
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void eventBusMain(String str){
        Log.i("TAG", "MAIN:"+str+" Thread="+Thread.currentThread().getId());
    }

    
    
    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void eventBusBg(String str){
        Log.i("TAG", "BACKGROUND:"+str+" Thread="+Thread.currentThread().getId());
    }

    
    @Subscribe(threadMode = ThreadMode.POSTING)
    public void eventBusPosting(String str){
        Log.i("TAG", "POSTING:"+str+" Thread="+Thread.currentThread().getId());
    }

    
    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void eventBusAsync(String str){
        Log.i("TAG", "ASYNC:"+str+" Thread="+Thread.currentThread().getId());
    }

1,調用getDefault(),裡面採用單利雙重鎖模式創建Eventbus對象

static volatile EventBus defaultInstance;
public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}

2,構造方法

2.1,粘性事件,保存到ConCurrenHashMap集合,(在構造方法中實現),
HashMap效率高,但線程不安全,在多線程的情況下,儘量用ConcurrentHashMap,避免多線程並發異常

EventBus(EventBusBuilder builder) {
    logger = builder.getLogger();
    subscriptionsByEventType = new HashMap<>();
    typesBySubscriber = new HashMap<>();
    stickyEvents = new ConcurrentHashMap<>(); 
    mainThreadSupport = builder.getMainThreadSupport();
    mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
    backgroundPoster = new BackgroundPoster(this);
    asyncPoster = new AsyncPoster(this);
    indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
    subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
            builder.strictMethodVerification, builder.ignoreGeneratedIndex);
    logSubscriberExceptions = builder.logSubscriberExceptions;
    logNoSubscriberMessages = builder.logNoSubscriberMessages;
    sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
    sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
    throwSubscriberException = builder.throwSubscriberException;
    eventInheritance = builder.eventInheritance;
    executorService = builder.executorService;
}

3,註冊register()方法主要做了2件事:

3.1,找到訂閱者的方法.找出傳進來的訂閱者的所有訂閱方法,然後遍歷訂閱者的方法.
A,通過反射來獲取訂閱者中所有的方法,並根據方法的類型,參數和註解找到訂閱方法.

3.2,訂閱者的註冊

public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
    
  List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            
             subscribe(subscriber, subscriberMethod);
        }
    }
}


private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        

,並根據方法的類型,參數和註解找到訂閱方法.
        methods = findState.clazz.getMethods();
        findState.skipSuperClasses = true;}

    

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);
        }
    }

3,事件發送post(),

public void post(Object event) {
  
   PostingThreadState postingState = currentPostingThreadState.get();
    
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);

    if (!postingState.isPosting) {
        postingState.isMainThread = isMainThread();
        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;
        }
    }
}

4,取消事件訂閱

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 { logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass()); }}

借鑑:劉望舒先生的進階之光

相關焦點

  • Android EventBus3 源碼解析 把控事件總線
    EventBus is a publish/subscribe event bus optimized for Android.若項目開啟混淆,則需要在proguard中加入如下代碼:-keepattributes*Annotation*-keepclassmembersclass**{@org.greenrobot.eventbus.Subscribe;}-keepenumorg.greenrobot.eventbus.ThreadMode
  • EventBus源碼詳解(一)
    "com.leo.eventbus.sample.SampleBusIndex" 4        verbose "true" 5    }6}build.gradle的完整代碼就不貼了,如果不懂可以從文末下載源碼參考。
  • EventBus 流程解析
    作者丨老王頭碎碎念https://www.jianshu.com/p/c64b2315db3cEventBus 源碼解析
  • EventBus3.0 使用及源碼解析
    優先級高的方法先被調用,在方法調用完成後可以調用EventBus.getDefault().cancelEventDelivery(event) ;終止優先級低的方法的調用。sticky為粘性事件,默認為關閉狀態。能夠收到訂閱之前發送到的最後一條消息,並且發送的方法不再是post()而是postSticky()。EventBus3.0源碼解析EventBus是Very的好用。
  • EventBus 原理深度解析
    二、框架解析2.1、組織結構eventbus的組織結構如下:eventbus主要有以下幾部分組成:1、eventbus、asyncEventBus:事件發送器。2、event:事件承載單元。3、SubscriberRegistry:訂閱者註冊器,將訂閱者註冊到event上,即將有註解Subscribe的方法和event綁定起來。4、Dispatcher:事件分發器,將事件的訂閱者調用來執行。5、Subscriber、SynchronizedSubscriber:訂閱者,並發訂閱還是同步訂閱。
  • EventBus—事件總線
    Vue EventBus 源碼解析VUE中EventBus可以用來進行任何組件之間的通信。EventBus可以當成一個管道,這個管道兩端可以接好多組件,兩端的任何一個組件都可以進行通信。這個管道就是Vue實例,實例中有四個跟事件派發相關的方法$on $off $emit $once。
  • 阿里P8架構師力薦的 Java源碼解析及面試合集
    那是因為你還不了解大廠的面試套路。Java 的底層實現是常被問到的,也就是 Java 源碼。如果啃不下來,很可能就與大廠失之交臂。有好多人認為閱讀源碼是不重要的,但如果是有追求,想進大廠的你, 閱讀源碼確實可以幫你順利技術面試,找到更好的工作。
  • Vue組件的通信--eventBus
    // event-bus.jsimport Vue from 'vue';const EventBus = new Vue();export default EventBus;此處完成的工作是我們創建了一個JavaScript ES6模塊,該模塊導入Vue,並創建和導出Vue的新實例。就這樣。
  • @EventListener--- Spring源碼從入門到精通(三十)
    上篇文章介紹實現ApplicationListener接口實現spring事件監聽:ApplicationListener--- Spring源碼從入門到精通
  • .NET編程之事件總線(Event Bus)知多少 (2)
    使用Castle Windsor使用IOC容器的目的很明確,一個是在註冊事件時完成依賴的注入,一個是在觸發事件時完成依賴的解析。從而完成事件的動態綁定和觸發。4.2.1. 初始化容器要在EventBus這個類中完成事件依賴的注入和解析,就需要在本類中持有一個對IWindsorContainer的引用。
  • 這個Github項目,搞定前端開發所有React 面試題
    在以往的文章中總結過Vue和Angualr相關的前端開發面試題,今天就來總結分享一下常考的 React 相關面試題和答案。這份React面試題總結來自Github,目前,這個 reactjs-interview-questions 項目在GitHub上已經獲得 1911 個Star,333個Fork。
  • 從 Event Loop 角度解讀 Vue NextTick 源碼
    點擊上方 前端瓶子君,關注公眾號回復算法,加入前端編程面試算法每日一題群來源:小卒先生https
  • Python國內外原題解析及源碼1~15
    Python國內外原題解析及源碼1~10請點擊標題打開前文:Python國外原題解析及源碼1~1011.題
  • EventBus 3.0+ 源碼詳解(史上最詳細圖文講解)(上)
    本文講解的是 'org.greenrobot:eventbus:3.1.1' 最新版,和其他版本有細微差別,思想都是一樣的。;    private boolean handlerActive;   protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {        super(looper);        this.eventBus = eventBus
  • android面試題- Volley源碼剖析
    android面試題-okhttp內核剖析與源碼相關面試題一通過一個小慄子慢慢剝開神秘的面紗。Override                     public void onErrorResponse(VolleyError error) {     }    }); queue.add(stringRequest);第一部分:一行行分析RequestQueue queue = Volley.newRequestQueue(this);進入源碼分析
  • Vue.js 3.x 源碼解析先導
    前言2018 年 6 月我在慕課網發布了 Vue.js 2.x 的源碼解析課程 《Vue.js 源碼全方位深入解析》,同時也開源了課程配套電子書。時隔一年多,Vue 官方也開源了 Vue.js 3.x,那麼在不久的將來,我也會系統化地做 Vue.js 3.x 的源碼分析,同時更新我的這門課程視頻以及電子書。
  • Jetpack源碼解析--ViewModel基本使用及源碼解析
    1.背景Jetpack源碼解析系列文章:1. Android_Jetpack組件---Naviagtion源碼解析2. Jetpack源碼解析—Navigation為什麼切換Fragment會重繪?3. Jetpack源碼解析---用Lifecycles管理生命周期4.
  • 這套1307頁的阿里、騰訊等大廠Android面試真題解析火了!
    下面的題目是一個大牛花了很長時間整理的群友在面試阿里、騰訊等網際網路大廠被問到的面試真題和答案解析,如果大家還有其他好的題目或者好的見解歡迎分享。參考解析:答案來自於平時的收集和各位群友的共同分享和校正。接下來我們看看一線大廠Android中高級面試展開的完整面試題
  • 玩轉EventBus,詳解其使用
    可以在任意線程任意位置發送事件,直接調用eventBus.post(Object) 方法,可以自己實例化 EventBus 對象,但一般使用默認的單例就好了:EventBus.getDefault(), 根據post函數參數的類型,會自動調用訂閱相應類型事件的函數。
  • ZooKeeper源碼學習筆記--client端解析
    ZooKeeper是一個相對簡單的分布式協調服務,通過閱讀源碼我們能夠更進一步的清楚分布式的原理。如上所述,client端其實是對 zookeeper.jar 的簡單封裝,在構造出一個ZooKeeper對象後,通過解析用戶輸入,調用 ZooKeeper 接口和 Server 進行交互。