前言
本文將介紹如何在CC框架下實現Fragment和View的元件化。
一、需求背景
在android元件化過程中,你有沒有遇到這樣的問題:
- 單Activity + 多Fragment的架構下,如何進行Fragment的元件化?
- 主介面上的Fragment太多,想用元件化進行管理,該怎麼做?
- 一個功能模組比較獨立,但在主介面使用了其中一個Fragment,如何解耦?
- 對某些View進行了封裝或者自定義的View,功能比較獨立,是作為基礎庫通過類依賴使用好還是作為元件使用比較好?如果要作為元件使用,那要如何元件化?
二、現有的一些解決方案:
- 在主app中依賴所有元件,所以在主app中可直接使用這些元件中的Fragment或View的類
- 直接使用具體的類將產生耦合,違背了元件化的解耦目的
- 元件之間的fragment引用也需要直接依賴,這樣就變成了一個庫而非元件
- 使用ARouter來獲取Fragment物件來實現Fragment元件化
- 由於沒有Fragment的具體型別,只能呼叫到系統中Fragment的public方法,不能進行業務通訊
- 由於View的建立需要用到Activity物件(用Application物件會導致Activity設定的Theme樣式失效),無法通過這種方式來獲取
- 建立一個公共庫,供所有元件依賴,所有元件在初始化時,將元件內的Fragment和View註冊到公共庫中生成一個對映表。元件通過呼叫公共庫的對映表查詢對應的Fragment或View的類
- 跟用ARouter獲取Fragment一樣,在元件中無具體型別時無法進行業務通訊
- 如果要用於獲取View,其構造方法的引數列表呼叫方需要了解,在一定程度上也屬於類耦合
- 使用ARouter的獲取Service方式實現,對外暴露服務,在公共庫中定義介面,提供建立、業務通訊相關的方法,在元件中實現此介面的具體功能,呼叫方通過動態獲取介面實現類來呼叫業務功能。這種方式能實現Fragment和View的元件化呼叫和業務通訊,實現的也比較優雅,但介面的管理成本有點高。
- 介面放在公共庫中,一般用以下2種方式實現:
- 為每個向外提供Fragment或View的元件額外建立一個公共庫,供需要呼叫的元件依賴(感覺好麻煩,還不如直接依賴元件...)
- 所有元件的這些介面統一放在一個公共庫中,供所有元件依賴。但這個庫的維護成本就比較高了,每次有新的介面或者原介面新增/修改方法都要修改這個庫。
- 介面放在公共庫中,一般用以下2種方式實現:
三、快速瞭解CC
- 是一套基於元件匯流排的元件化實施方案
- 一靜一動,開發時執行2個app,業務環境始終是完整的:
- 靜:主App (通過跨App的方式呼叫單元件App內的元件)
- 動:正在開發中的單元件App (通過跨App的方式呼叫主App內的元件)
- 支援漸進式元件化改造
- 解耦只是過程,而不是前提
四、在CC框架中如何實現Fragment的元件化?
CC的引數和回撥結果使用的資料結構是Map,在app內部可以傳遞任何型別。
-
通過CC呼叫獲取元件中的Fragment物件
1.1 元件呼叫方按如下方式呼叫,並從回撥結果中獲取Fragment,例如:
Fragment fragment = CC.obtainBuilder("ComponentName") .build().call().getDataItem("key"); if (fragment != null) { //show fragment } 複製程式碼
1.2 元件實現方按如下方式設定結果,例如:
CC.sendCCResult(cc.getCallId(), CCResult.success("key", new MyFragment())); 複製程式碼
-
與Fragment進行通訊
元件化實施的主要目的之一是業務隔離:只暴露呼叫協議給外部(類似於app端與服務端的通訊介面),內部實現的更改對外部無影響。甚至元件的插拔和替換都不影響呼叫方(只要元件呼叫方做好元件呼叫失敗的降級處理,例如1.1示例程式碼中的
if (fragment != null) {...}
。)所以,Fragment中的具體業務邏輯應由元件自身內部來實現,在元件呼叫方(如:Activity)中通過CC呼叫元件暴露的介面來完成。
2.1 元件呼叫方將fragment物件及其它引數通過CC傳遞給元件,例如:
boolean success = CC.obtainBuilder("ComponentName") .setActionName("updateTextView") //action名稱 .addParam("fragment", fragment) //目標fragment物件 .addParam("value", text) //設定引數 .build().call().isSuccess(); 複製程式碼
2.2 元件中接收fragment物件及其它引數,並呼叫fragment物件的指定方法實現對應的業務,例如:
@Override public boolean onCall(CC cc) { String actionName = cc.getActionName(); if ("updateTextView".equals(actionName)) { MyFragment fragment = cc.getParamItem("fragment");//接收fragment物件 if (fragment != null) { String text = cc.getParamItem("value", "");//接收其它引數 fragment.updateText(text);//呼叫fragment的方法 CC.sendCCResult(cc.getCallId(), CCResult.success());//回撥結果 } else { //回撥錯誤資訊 CC.sendCCResult(cc.getCallId(), CCResult.error("no fragment params")); } } return false; } 複製程式碼
五、 View有沒有必要元件化?
答案是:對於一些封裝過的View、自定義View(特別是第三方自定義View)是有必要的。
理由是:元件化能很好的解耦,將業務實現完全交給元件內部完成,只要介面協議不發生變化,實現方式發生改變時不會影響到使用方式。
網上很多元件化方案中,都是將自定義View(自己寫的或者第三方庫)作為公共庫來使用。如果沒有做個適配層(Adapter)而直接使用自定義View的類,將會導致View的耦合度很高,降低系統的擴充套件性。
六、在CC框架中如何實現View的元件化?
與Fragment元件化一樣,通過CC獲取物件和業務呼叫。
唯一的差別是:在獲取View物件時需要將Activity物件傳給元件
View view = CC.obtainBuilder("ComponentName")
.setContext(activity) //將activity物件傳給元件,用於View的初始化
.build().call().getDataItem("key");
if (view != null) {
//add view to container
}
複製程式碼
總結
關於android的元件化文章一般都只是介紹如何進行Activity的跳轉及服務呼叫,對於Fragment的元件化一直沒有很好的解決,View的元件化幾乎沒有被提到。
本文介紹了在CC元件化框架下實現Fragment及View元件化的方式,為android工程元件化的道路掃除一個障礙。