模塊化通訊方式對比

前面介紹了一個自定義的簡單Router組件,是使用了組件化的思想,那麼咱們常常說到的組件化和模塊化的區別是什麼呢?其實這兩個概念很相近,能夠這麼說組件化是模塊化,可是模塊化不必定是組件化。java

組件化的核心是角色的轉換。 在打包時是library,而在調試時是application,能夠這麼理解,組件是能夠單獨運行的模塊,具體的轉換方式在以前的文章已經介紹過了。那麼在抽出好模塊之後各模塊之間應該如何進行通訊呢?有人說想用哪一個模塊就直接引用,這是很是很差的方式,做者很是不推薦這種作法。由於若是其餘項目須要使用當前項目的某一個模塊的時候,若是該模塊有依賴其餘模塊就必須把該模塊依賴的全部模塊都複製過去,就出現了所謂的"拖油瓶"現象,靈活性太差,因此咱們應該避免出現這種雜亂無章的循環依賴。mysql


今天咱們是在模塊被app依賴的前提下,也就是各組件是library的狀況,講解一下各個模塊之間的通訊方式。做者能想到的使用較多的方式至少有三種,分別是EventBus、SPI、ARouter,固然還有其餘的方式,這裏就不一一介紹了。接下來做者帶領你們一塊兒分析一下這3種方式的優缺點,若是有更好的方式也但願你們分享出來。git

EventBus
github

EventBus可以簡化各組件間的通訊,有效的分離事件發送方和接收方(解耦),避免複雜和容易出錯的依賴性和生命週期問題。而且最重要的是使用起來很是簡單,具體的使用方式就不詳細介紹了,網上關於它的使用方式很是多,這裏就簡單貼個圖吧。sql

缺點:EventBus能夠發送消息到其餘模塊,可是並不能直接傳值回來。這裏若是必定要實現傳值也不是沒有辦法,好比再反發射一個事件回去,又或者經過接口回調的形式來實現。就拿接口回調來舉例子吧,每個事件都要寫一個回調接口,而不是直接獲取返回值,這樣就會以爲用起來很不舒服。更重要的是若是要發送多個消息的話,每一個消息須要定義一個event類,致使用起來就更不爽了。並且eventbus是發送給全部的訂閱者,而不是一對一,若是要實現一對一還要編寫更多額外代碼,整個代碼框架將會很冗餘。初次以外,EventBus發送的地方若是要尋找到使用的地方,或者使用的地方想要了解發送方,只能經過代碼全局搜索的方式,整個架構很是亂很影響閱讀效率。數據庫


SPI技術api

Service Provider Interface,是Java提供的一套用來被第三方實現或者擴展的API,它能夠用來啓用框架擴展和替換組件。好比JDBC就是經過SPI機制來加載不一樣的數據庫(mysql、oracle等)驅動,大體的機制以下圖。SPI分爲服務提供方和調用方,服務提供方須要提供好調用方須要的接口和調用路徑,另外須要在公共模塊定義好一個接口用來銜接兩個模塊。另外須要在base層定義一個接口做爲橋樑,一個寫一個讀。bash


接下來咱們以A模塊須要調用B模塊的數據爲例來說解一下整個過程,首先須要在B模塊中提供好A模塊須要的數據的返回方法以及接口實現類的調用路徑。注意調用路徑的提供方式是在src/main目錄下新建resources文件夾,而後再新建META-INF.services文件夾,而後在其中新建一個文件,注意這個文件名不能隨便取,必定要和base中定義的接口路徑如出一轍微信



而後在base模塊中定義一個接口,接口路徑和名稱和上面的文件名保持一致,接口中內容爲約定好的數據傳輸方法。架構



最後在A模塊中調用便可,注意這裏接口實現類是一個迭代器,說明能夠接收到多個實現的方法,全部實現了TestService接口,而且在META-INF下的文件裏寫入了的類都會被迭代器給遍歷出來,執行裏面的接口實現方法


至此SPI實現模塊間通訊已經實現完成,估計你們也發現了,使用SPI來實現是很是麻煩的一件事情,每傳一類值就要新建接口映射,並且使用處還要進行解析,若是業務大了須要傳的值太多的狀況下,這種方式同樣將成爲傳值噩夢。還有接口須要定義在base層,這其實有違base的建立初衷。base層的初衷是一些固定的代碼,在穩定後不須要修改,也和業務代碼沒有任何關係。SPI方式來傳值每次都須要在base建立接口,就意味着base層一直是須要修改的,並且將會愈來愈臃腫,這不是咱們想要看到的,因此做者也不推薦使用這種方式來進行模塊間傳值。那麼是否有其餘的更優秀的傳值框架呢?這就是咱們的終極武器——ARouter了,固然還有其餘大廠的Router框架,這裏就以ARouter爲例來說解一下ARouter來實現模塊傳值。


ARouter

如上圖,因爲其餘模塊須要調用用戶模塊的數據,因此須要對本屬於用戶模塊的AccountService進行接口下沉,將其放入base中。用戶模塊實現AccountService,其餘模塊因爲擁有base的權限,因此能夠調用用戶模塊中的實現類代碼。咱們假設在一個模塊化項目中,module1須要調用module2中的方法獲取到數據,只須要按如下步驟。

步驟一 引入ARouter包

在使用註解的module2中按照ARouter的github文檔走一遍就行了,github地址:https://github.com/alibaba/ARouter/blob/master/README_CN.md

api 'com.alibaba:arouter-api:1.4.1'
複製代碼
annotationProcessor 'com.alibaba:arouter-compiler:1.2.2'
複製代碼
javaCompileOptions {    
      annotationProcessorOptions {       
          arguments = [AROUTER_MODULE_NAME: project.getName()]    
       }
 }複製代碼


步驟二 在base模塊定義數據傳輸接口


注意一下,這裏須要繼承IProvider,用來引入Arouter的支持,相應的在TransDataInterface實現類中也須要實現init方法來初始化一些數據,沒有數據的話也能夠無論。


步驟三 在module2中實現該接口

注意,這裏須要用@Route(path="")來標註,用來給註解處理器生成類文件,編譯後生成的文件以下:


步驟四 其餘模塊使用該接口獲取數據


至此,按照文檔簡單地使用ARouter傳值已完成,可是咱們發現因爲須要接口下沉,業務接口須要在base層裏進行定義,這樣等業務愈來愈多的時候,base層又會愈來愈臃腫,咱們設計base初衷是穩定了就能夠不用動了,那麼是否有辦法能夠避免這個問題呢?答案就是建立中介api模塊。好比支付建立支付api模塊,用戶建立用戶api模塊,建立出來的api模塊能夠被任何一個大模塊引用。接下來以module1須要獲取module2中的值爲例,使用建立模塊的方式來實現模塊間傳值。

步驟一 建立module2_api模塊

新建一個module2_api的module,將數據傳值的接口複製到該模塊,注意須要在gradle中引入arouter,用以繼承provider類

步驟二 讓module一、module2全都依賴module2_api模塊

步驟三 在module2中引入arouter的支持、arouter註解處理器、arouter類名等,而後須要寫好數據傳輸的實現類方法,使用@router進行註解標記,這樣在編譯時就會生成類文件了。

步驟四 在module1中調用arouter的方法來獲取數據便可


那麼分離出api模塊的方式是否有缺點呢?答案是確定的,假如一個大項目中有20個模塊,那麼就須要手動建立20個api模塊,模塊數量瞬間翻倍了,使得項目變得更復雜。那麼微信是如何解決這個問題呢?答案就是微信.api化技術。微信是使用了自定義文件後綴名的方法,將接口的文件名修改成.api(這裏其實隨便改個名字都行,好比.abc都行),改爲.api後編譯時經過gradle腳本去讀取工程中全部.api文件,讀取到以後自動建立module_api工程,而且將接口文件自動複製到建立好的module_api工程中,而且再將名字修改回.java,具體的腳本大體以下:



總結:今天主要是對模塊化通訊的幾種方式進行了對比,因爲eventbus很難維護,因此咱們考慮了使用spi,而後spi又太麻煩,最終使用到了ARouter。使用之後發現每次都要新建api模塊進行通訊,因此微信就研究出來.api化技術,經過gradle腳原本操做每一個組件自動生成api模塊,這也是目前比較前沿的技術。固然技術沒有最好,只有更好,相信通過你們的努力,之後確定會出現更好的模塊化通訊框架,讓咱們拭目以待。

相關文章
相關標籤/搜索