CC框架實踐(2):Fragment和View的元件化

齊翊發表於2017-12-26

前言

本文將介紹如何在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的元件額外建立一個公共庫,供需要呼叫的元件依賴(感覺好麻煩,還不如直接依賴元件...)
      • 所有元件的這些介面統一放在一個公共庫中,供所有元件依賴。但這個庫的維護成本就比較高了,每次有新的介面或者原介面新增/修改方法都要修改這個庫。

三、快速瞭解CC

  • 是一套基於元件匯流排的元件化實施方案
  • 一靜一動,開發時執行2個app,業務環境始終是完整的:
    • 靜:主App (通過跨App的方式呼叫單元件App內的元件)
    • 動:正在開發中的單元件App (通過跨App的方式呼叫主App內的元件)
  • 支援漸進式元件化改造
    • 解耦只是過程,而不是前提

四、在CC框架中如何實現Fragment的元件化?

CC的引數和回撥結果使用的資料結構是Map,在app內部可以傳遞任何型別。

  1. 通過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()));
    複製程式碼
  2. 與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工程元件化的道路掃除一個障礙。

系列文章

CC:可關聯生命週期的android元件化開發框架

CC框架實踐(1):實現登入成功再進入目標介面功能

CC框架實踐(2):Fragment和View的元件化

CC框架實踐(3): 讓jsBridge更優雅

相關文章