CC 组件化开发

以前开发针对功能较多的应用,一般是通过分包的形式将各个模块进行解耦,然后将将通用的工具或者逻辑进行封装供其他模块使用,但是这样依然很难进行有效的解耦,因为其他包里面的类依然可以通过new的方式进行创建,很难进行把控,尤其针对各个功能模块可能需要单独上线的应用更是无法满足要求,不经意就会出现空指针异常。

来到现在的项目组之后接触了一个组件话开发的框架CC,一个可以实现组件动态组册,完成各个组件很好的隔离的框架,好处自然不用多说,此文章我们先大致介绍一下组件化的基本知识:

第一个问题:什么是组件化?

组件化这三个字顾名思义就是将一个项目拆成多个项目,也就是说一个项目由多个组件组成,就比如一辆汽车,你可以把它拆分成发动机、底盘、车身和电气设备等四个模块;又比如一架飞机你可以把它拆分成机身、动力装置、机翼、尾翼、起落装置、操纵系统和机载设备等7个模块,那么你在开发项目的时候是否也应该把你的项目根据业务的不同拆分成不同的模块,如果不同的模块可以单独运行的话,我们就可以叫它组件。

第二个问题:组件化开发有什么好处?

  • 现在市场上的app几乎都有个人中心这个东西,那么是不是可以把个人中心的所有业务都提到单独的一个模块中,当然是可以的,我们将它放在一个单独的模块中,这个时候你会发现一些好处:
  • 耦合度降低了
  • 你需要修改个人中心的时候直接从这个模块中找修改的地方就好了
  • 你需要测试的时候直接单独运行这个模块就好了,不需要运行整个项目(测试的效率是不是提高了很多呢)
  • 由于测试的时候可以单独编译某个模块编译速度是不是提高了呢
  • 如果是团队开发的话,每个人单独负责自己的模块就好了(妈妈再也不用担心我的代码被人家修改了)。

第三个问题:组件化开发的步骤(以我的demo目录为例,我的demo主要有一个主程序【app】和四个组件【component_base,libraryone,librarytwo,xpush】demo地址:


one.png

配置流程

1. 组件化开发肯定是有一个或某两个组件是各个组件都会引用的,一般我们会把log工具类、网络请求封装框架、自定义的view接口类、以及下沉的接口等封装成base组件供其他组件可以调用,我们可以在这个所有组件都会依赖的组件的build.gradle文件中依赖cc,方式如下:

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

2. 在项目的根目录下的build.gradle依赖自动注册插件:

    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. 在项目的根目录下新建cc-settings-2.gradle文件,键入以下内容:

project.apply plugin: 'cc-register'

4. 在非主项目的组件中替换原来的apply plugin ‘XXX’为以下:

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

5. 在主项目中替换原来的apply plugin ‘xxx’为以下:

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

6. 最后一步,也是最关键的一步,将各组件添加到主项目中(在app的build.gradle中添加),否则调用会失败:

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

开发流程

以上是依赖CC进行组件开发的配置流程,下面根据自己的项目说一下开发流程,我们以base组件和libraryone组件为例:

1. 由于在B组件可能存在调用A组件的一些实例,但是各个组件又都是项目独立的,所以需要将对外开放使用的对象抽象到base组件,我的demo如下:

two.png

其中的三个接口分别是在另外三个组件里面实现的,同时也把各个组件使用的常量也在base组件里面定义了ComponentConst类,方便外部组件调用:

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. 各个组件需要有个类实现IComponent接口,以libraryone为例:

public class Component_A implements IComponent {
    @Override
    public String getName() {
    //此出的名字是外部调用该组件时传入的名称,用于区分不同的组件
        return ComponentConst.Component_A.NAME;
    }

    @Override
    public boolean onCall(CC cc) {
    //此处的action是外部调用该组件内部的方法的标记,用于区分该组件内不同的功能或者方法,由于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里面实现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. 此时我们如果想在其他组件或者任何地方(非libraryone组件内)获取到ComponentAManager实例,发现new是不行的,getInstance也是不行的,也就是使用CC的一个好处,可以很好的隔离个组件的功能界限,那么我们怎么获取呢,用如下方法(此处我是在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());
                    }

                }

当然也可以仅仅在libraryone里面进行操作而不是返回一个对象,比较简单,可以自己琢磨下。今天主要讲了CC的使用,下一篇说结合CC的源码分享一下内部的实现原理。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容

  • 不怕跌倒,所以飞翔 组件化开发 参考资源 Android组件化方案 为什么要组件化开发 解决问题 实际业务变化非常...
    笔墨Android阅读 2,968评论 0 0
  • 问题 在已经开发过几个项目的童鞋,如果这时需要重新开发一个新项目,是否需要自己重新搭建框架呢,还是从老项目中拷贝粘...
    8ba406212441阅读 43,012评论 84 381
  • MVVMHabitComponent 关于Android的组件化,相信大家并不陌生,网上谈论组件化的文章,多如过江...
    goldze阅读 5,173评论 2 22
  • 1. App项目组件化 做移动开发的同学都会发现这两年在移动开发圈子里最火的就是组件化了,组件化不同的实现方案也引...
    monkey01阅读 10,543评论 6 56
  • 黑色情调 有一种感觉莫名其妙, 有一种爱情无法得到。 有一种执著傻的可笑, 有一种调叫黑色情调。 黑...
    生来彷徨ii阅读 318评论 0 2