CC 組件化開發(fā)

以前開發(fā)針對(duì)功能較多的應(yīng)用,一般是通過分包的形式將各個(gè)模塊進(jìn)行解耦,然后將將通用的工具或者邏輯進(jìn)行封裝供其他模塊使用,但是這樣依然很難進(jìn)行有效的解耦,因?yàn)槠渌锩娴念愐廊豢梢酝ㄟ^new的方式進(jìn)行創(chuàng)建,很難進(jìn)行把控,尤其針對(duì)各個(gè)功能模塊可能需要單獨(dú)上線的應(yīng)用更是無法滿足要求,不經(jīng)意就會(huì)出現(xiàn)空指針異常。

來到現(xiàn)在的項(xiàng)目組之后接觸了一個(gè)組件話開發(fā)的框架CC,一個(gè)可以實(shí)現(xiàn)組件動(dòng)態(tài)組冊(cè),完成各個(gè)組件很好的隔離的框架,好處自然不用多說,此文章我們先大致介紹一下組件化的基本知識(shí):

第一個(gè)問題:什么是組件化?

組件化這三個(gè)字顧名思義就是將一個(gè)項(xiàng)目拆成多個(gè)項(xiàng)目,也就是說一個(gè)項(xiàng)目由多個(gè)組件組成,就比如一輛汽車,你可以把它拆分成發(fā)動(dòng)機(jī)、底盤、車身和電氣設(shè)備等四個(gè)模塊;又比如一架飛機(jī)你可以把它拆分成機(jī)身、動(dòng)力裝置、機(jī)翼、尾翼、起落裝置、操縱系統(tǒng)和機(jī)載設(shè)備等7個(gè)模塊,那么你在開發(fā)項(xiàng)目的時(shí)候是否也應(yīng)該把你的項(xiàng)目根據(jù)業(yè)務(wù)的不同拆分成不同的模塊,如果不同的模塊可以單獨(dú)運(yùn)行的話,我們就可以叫它組件。

第二個(gè)問題:組件化開發(fā)有什么好處?

  • 現(xiàn)在市場(chǎng)上的app幾乎都有個(gè)人中心這個(gè)東西,那么是不是可以把個(gè)人中心的所有業(yè)務(wù)都提到單獨(dú)的一個(gè)模塊中,當(dāng)然是可以的,我們將它放在一個(gè)單獨(dú)的模塊中,這個(gè)時(shí)候你會(huì)發(fā)現(xiàn)一些好處:
  • 耦合度降低了
  • 你需要修改個(gè)人中心的時(shí)候直接從這個(gè)模塊中找修改的地方就好了
  • 你需要測(cè)試的時(shí)候直接單獨(dú)運(yùn)行這個(gè)模塊就好了,不需要運(yùn)行整個(gè)項(xiàng)目(測(cè)試的效率是不是提高了很多呢)
  • 由于測(cè)試的時(shí)候可以單獨(dú)編譯某個(gè)模塊編譯速度是不是提高了呢
  • 如果是團(tuán)隊(duì)開發(fā)的話,每個(gè)人單獨(dú)負(fù)責(zé)自己的模塊就好了(媽媽再也不用擔(dān)心我的代碼被人家修改了)。

第三個(gè)問題:組件化開發(fā)的步驟(以我的demo目錄為例,我的demo主要有一個(gè)主程序【app】和四個(gè)組件【component_base,libraryone,librarytwo,xpush】demo地址:


one.png

配置流程

1. 組件化開發(fā)肯定是有一個(gè)或某兩個(gè)組件是各個(gè)組件都會(huì)引用的,一般我們會(huì)把log工具類、網(wǎng)絡(luò)請(qǐng)求封裝框架、自定義的view接口類、以及下沉的接口等封裝成base組件供其他組件可以調(diào)用,我們可以在這個(gè)所有組件都會(huì)依賴的組件的build.gradle文件中依賴cc,方式如下:

dependencies {
   ...
    //以下是依賴CC
   *** api 'com.billy.android:cc:2.1.5'***
}

2. 在項(xiàng)目的根目錄下的build.gradle依賴自動(dòng)注冊(cè)插件:

    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.0'
        classpath 'com.billy.android:cc-register:1.0.9'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }

3. 在項(xiàng)目的根目錄下新建cc-settings-2.gradle文件,鍵入以下內(nèi)容:

project.apply plugin: 'cc-register'

4. 在非主項(xiàng)目的組件中替換原來的apply plugin ‘XXX’為以下:

ext.alwaysLib = true
apply from: rootProject.file('cc-settings-2.gradle')

5. 在主項(xiàng)目中替換原來的apply plugin ‘xxx’為以下:

ext.mainApp = true
project.apply plugin: 'cc-register'

6. 最后一步,也是最關(guān)鍵的一步,將各組件添加到主項(xiàng)目中(在app的build.gradle中添加),否則調(diào)用會(huì)失敗:

dependencies {
    ....
    
    api project(':component_base')
    addComponent 'libraryone'
    addComponent 'component_base'
    addComponent 'librarytwo'
    addComponent 'xpush'
}

開發(fā)流程

以上是依賴CC進(jìn)行組件開發(fā)的配置流程,下面根據(jù)自己的項(xiàng)目說一下開發(fā)流程,我們以base組件和libraryone組件為例:

1. 由于在B組件可能存在調(diào)用A組件的一些實(shí)例,但是各個(gè)組件又都是項(xiàng)目獨(dú)立的,所以需要將對(duì)外開放使用的對(duì)象抽象到base組件,我的demo如下:

two.png

其中的三個(gè)接口分別是在另外三個(gè)組件里面實(shí)現(xiàn)的,同時(shí)也把各個(gè)組件使用的常量也在base組件里面定義了ComponentConst類,方便外部組件調(diào)用:

public class ComponentConst {

    public interface Component_A{
        String NAME = "Component_A";

        public interface Action{
            String SHOW = "Component_A_show";
            String HIDE = "Component_A_hide";
        }

    }

    public interface Component_B{
        String NAME = "Component_B";

        public interface Action{
            String SHOW = "Component_B_show";
            String HIDE = "Component_B_hide";
        }

    }

    public interface Component_C{
        String NAME = "Component_C";

        public interface Action{
            String ShOW = "Component_C_show";
            String HIDE = "Component_CChide";
            String CONTENT = "setContent";
        }
    }
}

2. 各個(gè)組件需要有個(gè)類實(shí)現(xiàn)IComponent接口,以libraryone為例:

public class Component_A implements IComponent {
    @Override
    public String getName() {
    //此出的名字是外部調(diào)用該組件時(shí)傳入的名稱,用于區(qū)分不同的組件
        return ComponentConst.Component_A.NAME;
    }

    @Override
    public boolean onCall(CC cc) {
    //此處的action是外部調(diào)用該組件內(nèi)部的方法的標(biāo)記,用于區(qū)分該組件內(nèi)不同的功能或者方法,由于libraryone依賴了base組件,所以可以直接引用base組件里的常量
        String action = cc.getActionName();
        switch (action) {
            case ComponentConst.Component_A.Action.SHOW:
                ComponentAManager componentAManager = ComponentAManager.getInstance();
                CC.sendCCResult(cc.getCallId(),CCResult.successWithNoKey(componentAManager));
                break;
            case ComponentConst.Component_A.Action.HIDE:
                break;
        }

        return true;
    }
}

3. 在libraryone里面實(shí)現(xiàn)base中定義的IComponentAManager接口:

public class ComponentAManager implements IComponentAManager {

    private static final String TAG = "ComponentAManager";
    private static ComponentAManager componentAManager;
    private ComponentAManager(){}
    public static ComponentAManager getInstance(){
        if (componentAManager == null){
            synchronized (ComponentAManager.class){
                if (componentAManager == null){
                    componentAManager = new ComponentAManager();
                }
            }
        }
        return componentAManager;
    }
    
    private UserBean getUserBeanFromComponentA(){
        Log.d(TAG, "getUserBeanFromComponentA: ");
        UserBean userBean = new UserBean("ComponentA",18,180.7f);
        return userBean;
    }

    @Override
    public UserBean show() {
        Log.d(TAG, "show: ");
        return getUserBeanFromComponentA();
    }

    @Override
    public void set(UserBean userBean) {

    }
}

4. 此時(shí)我們?nèi)绻朐谄渌M件或者任何地方(非libraryone組件內(nèi))獲取到ComponentAManager實(shí)例,發(fā)現(xiàn)new是不行的,getInstance也是不行的,也就是使用CC的一個(gè)好處,可以很好的隔離個(gè)組件的功能界限,那么我們?cè)趺传@取呢,用如下方法(此處我是在app主程序中獲取的):

          CCResult ccResult = CC.obtainBuilder(ComponentConst.Component_A.NAME)
                        .setActionName(ComponentConst.Component_A.Action.SHOW)
                        .build()
                        .call();
                //是否獲取成功
                if (ccResult.isSuccess()){
                    IComponentAManager componentAManager = ccResult.getDataItemWithNoKey();
                    UserBean userBean = componentAManager.show();
                    if (userBean != null){
                        tv.setText("");
                        tv.setText("name:"+userBean.name+"\n"
                        +"age:"+userBean.age+"\n"
                        +"height:"+userBean.getHeight());
                    }

                }

當(dāng)然也可以僅僅在libraryone里面進(jìn)行操作而不是返回一個(gè)對(duì)象,比較簡(jiǎn)單,可以自己琢磨下。今天主要講了CC的使用,下一篇說結(jié)合CC的源碼分享一下內(nèi)部的實(shí)現(xiàn)原理。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 不怕跌倒,所以飛翔 組件化開發(fā) 參考資源 Android組件化方案 為什么要組件化開發(fā) 解決問題 實(shí)際業(yè)務(wù)變化非常...
    筆墨Android閱讀 3,095評(píng)論 0 0
  • 問題 在已經(jīng)開發(fā)過幾個(gè)項(xiàng)目的童鞋,如果這時(shí)需要重新開發(fā)一個(gè)新項(xiàng)目,是否需要自己重新搭建框架呢,還是從老項(xiàng)目中拷貝粘...
    8ba406212441閱讀 43,555評(píng)論 84 381
  • MVVMHabitComponent 關(guān)于Android的組件化,相信大家并不陌生,網(wǎng)上談?wù)摻M件化的文章,多如過江...
    goldze閱讀 5,784評(píng)論 2 22
  • 1. App項(xiàng)目組件化 做移動(dòng)開發(fā)的同學(xué)都會(huì)發(fā)現(xiàn)這兩年在移動(dòng)開發(fā)圈子里最火的就是組件化了,組件化不同的實(shí)現(xiàn)方案也引...
    monkey01閱讀 11,040評(píng)論 6 56
  • 黑色情調(diào) 有一種感覺莫名其妙, 有一種愛情無法得到。 有一種執(zhí)著傻的可笑, 有一種調(diào)叫黑色情調(diào)。 黑...
    生來彷徨ii閱讀 398評(píng)論 0 2

友情鏈接更多精彩內(nèi)容