幫助正在對項目進行組件化改造或者想創建組件化項目架構的小夥伴,更好的認識組件化本質。android
目前組件化的框架衆多,說的天花亂墜的,其本質來講其實都差很少,閱讀本文之後,讀者甚至能夠摒棄這些開源框架,根據本身的項目特色,輕鬆構建本身的組件化框架。git
幫助想學習和了解組件化框架,並嘗試動手寫本身的開源框架的小夥伴們github
在平時的開發過程當中,隨着項目需求的增長,app支持功能愈來愈多,若是沒有組件化的思想,代碼會愈來愈臃腫。致使的問題也會愈來愈明顯,好比一個很小的需求變化,可能會致使代碼的「牽一髮動全身」問題,甚至會出現不少隱藏的bug。還會致使維護成本愈來愈高,團隊協做低效,問題邊界不清晰。算法
因而,不少團隊開始有了代碼解耦的想法,可是面對如此複雜的項目,又不敢輕易變動其中的代碼結構,如何順利的解耦就成了不少團隊難以入手的問題。甚至因爲業務的不斷迭代,致使代碼解耦的問題遙遙無期。數據庫
固然,做爲一位合格的App架構師,遇到再難的問題也會迎難而上。因而便開始對APP總體項目結構進行分析,制定解耦方案。一般狀況下,解耦的最佳思路就是根據業務邊界對代碼結構進行劃分。好比說某APP裏面包含了IM、直播、內容展現等等業務場景,因而從架構的角度來講,整個APP的架構應該是以下圖所示:bash
這種架構思路上很清晰,對應到咱們Android代碼結構,就是根據這些業務邊界,拆分紅不一樣的module,module之間沒有直接的引用和依賴,代碼徹底解耦。做爲團隊開發成員也有很清晰的業務邊界,代碼維護成本大大下降,開發效率也會明顯提升,應該是一個很不錯的方案。網絡
所謂的組件化其實就是根據業務邊界對代碼進行解耦,不一樣的業務對應不一樣的module,這些module相互獨立,能夠獨自做爲一個app進行開發並獨立運行,合併時能夠打包到一個總體的app中,實現完整的app功能。架構
那麼問題來了,以上的架構的確是很是不錯的選擇,可是實際的業務中,很難有個清晰的邊界,而且業務與業務直接總會有銜接的地方。若是使用以上的架構,那麼這些不一樣的module之間又該如何進行調用呢?app
在咱們Android系統中,進程是一個獨立程序,每一個進程都具備本身的虛擬機 (VM),應用代碼是在與其餘應用隔離的環境中運行,進程直接的通訊主要是基於Binder機制。爲何要提Binder,首先Binder是Android系統的中很是重要的實現機制,而咱們組件化代碼耦合的問題也能夠借鑑其實現原理。接下來我簡單的介紹一下Binder機制,先整體看一下Binder架構圖:框架
能夠看出Binder是一個典型的CS架構,進程間的通訊基於ServiceManager這個大管家,Client進程從ServiceManager中獲取Server進程的一個遠程代理,進行通訊。爲了讓你們更直接的理解,我從代碼層面上來簡單描述一下這個過程。好比咱們啓動一個Activity時,須要ActivityManagerService(AMS)這個服務來進行管理,而AMS運行在SystemServer中,那麼如何獲取這AMS呢,咱們從源碼來分析(如下源碼android-28中):
public static IActivityManager getService() { return IActivityManagerSingleton.get();}private static final Singleton<IActivityManager> IActivityManagerSingleton = new Singleton<IActivityManager>() { @Override protected IActivityManager create() { //經過ServiceManager.getService獲取到AMS的代理IActivityManager final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE); final IActivityManager am = IActivityManager.Stub.asInterface(b); return am; }};複製代碼
app中經過ServiceManager.getService獲取到遠程進程中服務的代理,只須要指定服務的name就能夠。
Binder機制就不展開講解,若是想了解更多的同窗,能夠加入到ARetrofit的QQ羣中@我進行交流。接下來咱們再回到「如何優雅的進行組件化」這個問題上。
在瞭解Binder機制後,對於咱們組件化過程當中解耦代碼如何通訊,其實也能夠採用相似的機制。固然咱們的module之間並無跨進程,不會有跨進程通訊的問題。
咱們能夠將圖1中的每個module想象成一個服務,那麼module之間的通訊其實就相似於服務之間的通訊。而服務之間的通訊其實也不須要直接進行類的引用,只須要拿到另外一個module的服務代理,經過這個代理進行通訊。這裏又出了一個新的問題,如何實現提供代理服務的代理管家呢,其實這也相似於Binder裏面的ServiceManager,用如下這張架構圖來講明:
上圖中IM module註冊本身的IM服務到ServiceManager中,直播 module想要和IM module只須要獲取IM的服務代理就能夠進行操做。註冊的過程,對應到實際的代碼中,其實就是中ServiceManager這個管家的Module中聲明本身的服務接口,並在本身的module中實現這一接口。其餘module只須要在運行時拿到接口實現類的實例對象就能夠完成這一過程了,這一部分其實能夠參考ARetrofit README中 四 高階用法
中登陸服務接口的聲明與註冊過程就能夠了解。
固然,這篇文章不只僅讓你們瞭解別人已經開源好的框架,其實相似的框架不少,如ARetrofit 、ARouter、CC等開源,無關star量,這都是開源遲早和推廣的問題,其實本質上都是基於以上的原理,區別就是上層的封裝的問題,最終呈現的API是否簡潔,是否符合本身的要求,可否直觀的進行代碼維護。
這裏我將帶着你們動手一塊兒實現本身的ServiceManager管家。
第一步,咱們在ServiceManager中註冊不一樣module的服務接口,以下:
public interface ILoginManager { void login(); User getUser();}複製代碼
第二步,在Login Module中實現該接口,以下:
@Inject //須要自動注入服務的聲明public class LoginManagerService implement ILoginManager { @Override void login(Activity activity) { Intent intent = new Intent(activity, LoginActivity.class); activity.startActivity(intent); } @Override User getUser(CallBack callback) { //...網絡或者 或者 本地數據庫 等回去異步返回或者同步返回結果 }}複製代碼
第三步, 在直播 Module中跨Module獲取ILoginManager服務實例對象,這裏須要經過Android Studio自動注入框架[AInject]AInject(https://github.com/yifei8/AInject),完成跨Module自動注入流程,可參考(https://github.com/yifei8/AInject)[AInject]用法,具體實現以下:
public class ServiceManager implements InjectContract { private static class ServiceManager { private static final ServiceManager instance = new ServiceManager(); } static ServiceManager getInstance() { return InstanceHolder.instance; } /** * @Fixme 這裏建議使用實現LRU算法的列表存儲 */ List<Object> services = new ArrayList(); /** * * "@Inject" 註解標示的class 最終都會注入到該"@IMethod"註解標示過的方法中 * 注:"@IMethod"註解標示過的方法將由編譯器自動注入實現代碼,注入最終的代碼以下如: * * @IMethod * public void iMethodName() { * injectClass("injectClassName1") * injectClass("injectClassName2") * injectClass("injectClassName3") * injectClass("injectClassName4") * } * * 用戶能夠在該方法中經過反射完成本身的業務需求 * @param className class name */ @IMethod public void startInject() { } @Override public void injectClass(String serviceClassName) { services.clear() services.add(className); } /** * 獲取登陸服務,可在任意Module直接獲取該服務的實例化對象 */ public static ILoginManager getILoginManager() { if (getInstance().service.size() == 0) { getInstance().startInject(); } for (String className: getInstance().services) { try { Class<?> clazz = Class.forName(className); Object obj = clazz.getConstructor().newInstance(); if (obj instanceof ILoginManager) { return obj; } else { obj = null; }} catch (Exception e) { e.printStackTrace(); } } return null; }}複製代碼
其實就是這麼簡單,一個組件化的框架就完成了。
看到這裏的小夥伴們,大概已經理解了如何進行解耦Module直接的通訊工做了吧。想一想平時有沒有遇到其餘關於高耦合的代碼須要解耦的,均可以參考這種思路哦。
前面教你們如何進行組件化,已經如何實現組件化,其實還忽略了一個很是重要的問題,就是如何對現有的項目進行組件化。現有的項目通常都已經累計了不少代碼量,若是一次性根據業務進行拆解處理,解耦合顯然是不合實際的。那麼到底該怎麼作呢?
其實有了以上自定義的組件化框架ARetrofit,其實組件化並非一個版本就須要完成的。組件化的第一步固然仍是根據業務邊界來架構本身的APP框架,不一樣的Module中聲明好本身的服務。而組件化的工做能夠拆分到不一樣的迭代版本中,對於新增的業務明確到對應的業務Module中開發,對應老的業務代碼若是耦合度比較高的,不建議直接修改邏輯,能夠先將這部分代碼耦合的地方抽象到服務接口中,經過服務調用來實現調用過程。並在將來的版本中逐步進行剝離解耦最終實現真正的代碼隔離,以服務的形式完成業務間的交集部分。
前面講了不少,咱們再回到主題,相信你們對於「如何實現本身的Android組件化改造」應該有了很清晰的步驟和流程來吧。此外僅表明我的的一些拙見,若是意見或者建議歡迎進ARetrofit賜教。
轉載自「安卓巴士開發者門戶」