前言
本文將介紹如何在CC框架下實(shí)現(xiàn)Fragment和View的組件化。
一、需求背景
在android組件化過程中,你有沒有遇到這樣的問題:
- 單Activity + 多Fragment的架構(gòu)下,如何進(jìn)行Fragment的組件化?
- 主界面上的Fragment太多,想用組件化進(jìn)行管理,該怎么做?
- 一個(gè)功能模塊比較獨(dú)立,但在主界面使用了其中一個(gè)Fragment,如何解耦?
- 對(duì)某些View進(jìn)行了封裝或者自定義的View,功能比較獨(dú)立,是作為基礎(chǔ)庫通過類依賴使用好還是作為組件使用比較好?如果要作為組件使用,那要如何組件化?
二、現(xiàn)有的一些解決方案:
- 在主app中依賴所有組件,所以在主app中可直接使用這些組件中的Fragment或View的類
- 直接使用具體的類將產(chǎn)生耦合,違背了組件化的解耦目的
- 組件之間的fragment引用也需要直接依賴,這樣就變成了一個(gè)庫而非組件
- 使用ARouter來獲取Fragment對(duì)象來實(shí)現(xiàn)Fragment組件化
- 由于沒有Fragment的具體類型,只能調(diào)用到系統(tǒng)中Fragment的public方法,不能進(jìn)行業(yè)務(wù)通信
- 由于View的創(chuàng)建需要用到Activity對(duì)象(用Application對(duì)象會(huì)導(dǎo)致Activity設(shè)置的Theme樣式失效),無法通過這種方式來獲取
- 創(chuàng)建一個(gè)公共庫,供所有組件依賴,所有組件在初始化時(shí),將組件內(nèi)的Fragment和View注冊(cè)到公共庫中生成一個(gè)映射表。組件通過調(diào)用公共庫的映射表查找對(duì)應(yīng)的Fragment或View的類
- 跟用ARouter獲取Fragment一樣,在組件中無具體類型時(shí)無法進(jìn)行業(yè)務(wù)通信
- 如果要用于獲取View,其構(gòu)造方法的參數(shù)列表調(diào)用方需要了解,在一定程度上也屬于類耦合
- 使用ARouter的獲取Service方式實(shí)現(xiàn),對(duì)外暴露服務(wù),在公共庫中定義接口,提供創(chuàng)建、業(yè)務(wù)通信相關(guān)的方法,在組件中實(shí)現(xiàn)此接口的具體功能,調(diào)用方通過動(dòng)態(tài)獲取接口實(shí)現(xiàn)類來調(diào)用業(yè)務(wù)功能。這種方式能實(shí)現(xiàn)Fragment和View的組件化調(diào)用和業(yè)務(wù)通信,實(shí)現(xiàn)的也比較優(yōu)雅,但接口的管理成本有點(diǎn)高。
- 接口放在公共庫中,一般用以下2種方式實(shí)現(xiàn):
- 為每個(gè)向外提供Fragment或View的組件額外創(chuàng)建一個(gè)公共庫,供需要調(diào)用的組件依賴(感覺好麻煩,還不如直接依賴組件...)
- 所有組件的這些接口統(tǒng)一放在一個(gè)公共庫中,供所有組件依賴。但這個(gè)庫的維護(hù)成本就比較高了,每次有新的接口或者原接口新增/修改方法都要修改這個(gè)庫。
- 接口放在公共庫中,一般用以下2種方式實(shí)現(xiàn):
三、先簡單介紹一下CC框架
CC: Component Caller(github地址),是一個(gè)可跨app進(jìn)行組件調(diào)用的android組件化開發(fā)框架,可支持包括Activity跳轉(zhuǎn)、數(shù)據(jù)獲取、網(wǎng)絡(luò)請(qǐng)求等等幾乎所有指令的跨組件調(diào)用。
組件實(shí)現(xiàn)方可自行決定是同步實(shí)現(xiàn)還是異步實(shí)現(xiàn)。
調(diào)用方可自行決定是同步調(diào)用還是異步調(diào)用(<font color=red>Notice: 不要在主線程同步調(diào)用耗時(shí)操作</font>)。
并且組件調(diào)用可關(guān)聯(lián)Activity和Fragment的生命周期(在onDestroy被調(diào)用時(shí)自動(dòng)取消調(diào)用,避免出現(xiàn)內(nèi)存泄露)
CC框架的使用方式,可查看github中的 README文檔
CC框架的實(shí)現(xiàn)原理 詳細(xì)介紹
四、在CC框架中如何實(shí)現(xiàn)Fragment的組件化?
CC的參數(shù)和回調(diào)結(jié)果使用的數(shù)據(jù)結(jié)構(gòu)是Map,在app內(nèi)部可以傳遞任何類型。
-
通過CC調(diào)用獲取組件中的Fragment對(duì)象
1.1 組件調(diào)用方按如下方式調(diào)用,并從回調(diào)結(jié)果中獲取Fragment,例如:
Fragment fragment = CC.obtainBuilder("ComponentName") .build().call().getDataItem("key"); if (fragment != null) { //show fragment }1.2 組件實(shí)現(xiàn)方按如下方式設(shè)置結(jié)果,例如:
CC.sendCCResult(cc.getCallId(), CCResult.success("key", new MyFragment())); -
與Fragment進(jìn)行通信
組件化實(shí)施的主要目的之一是業(yè)務(wù)隔離:<b>只暴露調(diào)用協(xié)議給外部</b>(類似于app端與服務(wù)端的通信接口),內(nèi)部實(shí)現(xiàn)的更改對(duì)外部無影響。甚至組件的插拔和替換都不影響調(diào)用方(只要組件調(diào)用方做好組件調(diào)用失敗的降級(jí)處理,例如1.1示例代碼中的
if (fragment != null) {...}。)所以,F(xiàn)ragment中的具體業(yè)務(wù)邏輯應(yīng)由組件自身內(nèi)部來實(shí)現(xiàn),在組件調(diào)用方(如:Activity)中通過CC調(diào)用組件暴露的接口來完成。
2.1 組件調(diào)用方將fragment對(duì)象及其它參數(shù)通過CC傳遞給組件,例如:
boolean success = CC.obtainBuilder("ComponentName") .setActionName("updateTextView") //action名稱 .addParam("fragment", fragment) //目標(biāo)fragment對(duì)象 .addParam("value", text) //設(shè)置參數(shù) .build().call().isSuccess();2.2 組件中接收fragment對(duì)象及其它參數(shù),并調(diào)用fragment對(duì)象的指定方法實(shí)現(xiàn)對(duì)應(yīng)的業(yè)務(wù),例如:
@Override public boolean onCall(CC cc) { String actionName = cc.getActionName(); if ("updateTextView".equals(actionName)) { MyFragment fragment = cc.getParamItem("fragment");//接收fragment對(duì)象 if (fragment != null) { String text = cc.getParamItem("value", "");//接收其它參數(shù) fragment.updateText(text);//調(diào)用fragment的方法 CC.sendCCResult(cc.getCallId(), CCResult.success());//回調(diào)結(jié)果 } else { //回調(diào)錯(cuò)誤信息 CC.sendCCResult(cc.getCallId(), CCResult.error("no fragment params")); } } return false; }
五、 View有沒有必要組件化?
答案是:對(duì)于一些封裝過的View、自定義View(特別是第三方自定義View)是有必要的。
理由是:組件化能很好的解耦,將業(yè)務(wù)實(shí)現(xiàn)完全交給組件內(nèi)部完成,只要接口協(xié)議不發(fā)生變化,實(shí)現(xiàn)方式發(fā)生改變時(shí)不會(huì)影響到使用方式。
網(wǎng)上很多組件化方案中,都是將自定義View(自己寫的或者第三方庫)作為公共庫來使用。如果沒有做個(gè)適配層(Adapter)而直接使用自定義View的類,將會(huì)導(dǎo)致View的耦合度很高,降低系統(tǒng)的擴(kuò)展性。
六、在CC框架中如何實(shí)現(xiàn)View的組件化?
與Fragment組件化一樣,通過CC獲取對(duì)象和業(yè)務(wù)調(diào)用。
唯一的差別是:在獲取View對(duì)象時(shí)需要將Activity對(duì)象傳給組件
View view = CC.obtainBuilder("ComponentName")
.setContext(activity) //將activity對(duì)象傳給組件,用于View的初始化
.build().call().getDataItem("key");
if (view != null) {
//add view to container
}
總結(jié)
關(guān)于android的組件化文章一般都只是介紹如何進(jìn)行Activity的跳轉(zhuǎn)及服務(wù)調(diào)用,對(duì)于Fragment的組件化一直沒有很好的解決,View的組件化幾乎沒有被提到。
本文介紹了在CC組件化框架下實(shí)現(xiàn)Fragment及View組件化的方式,為android工程組件化的道路掃除一個(gè)障礙。
系列文章
CC框架實(shí)踐(1):實(shí)現(xiàn)登錄成功再進(jìn)入目標(biāo)界面功能