Android-组件化开发

参考资料:
https://www.jianshu.com/p/60c1b9ddd8ab

上一篇我们学习了ARouter,讲到ARouter是组件化开发的基础,那现在让我们开始组件化开发吧。

1.组件化,模块化概念

对于组件化的开发,首先要了解模块化及组件化的概念,这正是是好多小伙伴模糊的,所以我们有必要说明一下。

1.1 组件

组件的英文单词是component,意思是组件、部件、元件。在App工程上,件是构成业务或者功能模块的基本单位也就是组件不能被继续拆分,原则上,组件与组件之间互不依赖。比如我们的图片上传功能,可以叫图片上传组件,但不能叫图片上传模块,而且组件具有可替换性和重复利用性,可替换性指比如我们的地图定位组件可以用百度的,也可以用高德的。重复性是指我的地位功能可能在首页被调用,在其他页面也要被调用。

1.2 模块

模块的英文单词是Module,由多个组件构成。也就是从粒度上来看,模块要比组件大,也就是模块包含组件。举个例子:以安居客为例,安居客app内有二手房和新房相关功能, 这个二手房和新房就属于模块,但这二手房和新房模块都用到了分享房源的功能,而这个分享功能就属于组件了。

1.3 组件和模块的关系

image.png

我们以上图为例简单说一下,首页界面的天猫,聚划算,饿了么都属于模块同时也是一个业务,我们可以叫做业务模块,而我们继续点击天猫进入天猫超市,里面的分享功能,支付所用到的sdk等都算组件。那我们总结一下:模块化和组件化只不过是我们根据项目的需求,定义不同,一个工程可以由多个模块组成,每个模块可以由多个组件构成,模块可以单独存在,正是多个模块构成了整个项目。不论是模块化还是组件化,它们的目的都是把项目解耦便于代码的管理。高内聚、低耦合使开发人员分工明确,提高开发效率。

1.4 业务

我们上面说了模块也可以叫做业务模块,那么什么是业务呢,我们还是以上图为例:在淘宝的首页天猫,聚划算,充值中心等这些看起来完全不同的业务我们称之为Business业务。而天猫点击进入会有搜索业务,预约功能,签到等功能,聚划算点击进入也会有搜索业务,预约功能,签到等功能,像这种业务我们称之为基础业务。我们来张图说明一下Business业务和基础业务及组件之间的关系:

image.png

图中Business业务公用基础业务,而基础业务的实现可能依赖于某组件。

1.4 Library

我们刚才说了组件,比如上图提到的图片上传组件,网络组件,还有分享组件等,那这里要介绍另外一个概念:Library。你比如我们的分享组件用的是友盟分享sdk,我们的网络组件依赖的是okhttp库,这些依赖的三方库都称之为Library

2.组件化实践

参考资料:
https://mp.weixin.qq.com/s/-gC8JpmmCZWzcOsH5ZzLtQ
https://mp.weixin.qq.com/s/8_8gGpkpO2QFNkWgSRBwIg
我准备从以下几个方面来介绍组件化:

  • 代码解耦
  • 组件或模块的单独运行
  • 数据的传递与ui跳转
  • 生命周期管理

2.1代码解耦

ok,我们先看看我们的demo实现效果,就先简单上张图吧,不整gif图片了:

image.png

你看到的第一眼可能觉得简单,没什么大不了,但是这不同于我们平时的实现方法,平时我们的首页,资讯,我的都是在app中实现,但今天我们的目录结构是这样的:


image.png

我先来说一下各个目录的含义:

  • app
    app模块是我们的壳工程,平时我们最核心的代码都要往这里写,但今天不一样,app模块的职能改变了,它最主要的目的是对其他模块进行整合,确保app能正常运行,里面只有一些简单的代码。
  • commonlib
    commonlib 是一个依赖的Library,为什么说她是library呢?因为它的职能是把整个project所用到的library,公用的lib都放到这里,供其它模块引用,就不需要每个模块都写一遍了。以demo为例:


    image.png

注意:采用api代替implementation代替的目的就是为了让其他模块能够引用到

  • module_home,module_zixun,module_user,
    这三个模块是整个project最核心的地方, 对应我们上面效果图中的首页,资讯,我的三个tab。
    这样做的好处显而易见 :张三开发首页,李四开发资讯,王五开发我的 互不影响,提高开发效率 ,这也是组件化的优势
  • x5webview
    x5webview是作为组件存在的,是我基于腾讯TBS浏览服务封装的(类似webview作用),和分享组件,网路请求组件是一个级别,为了其他模块的调用。

这里就基本实现了代码的解耦,你可能有一个疑问?你这一会组件一会模块是不是有点懵,其实个人认为组件化和模块化只不过是概念不同,模块化包含组件化,组件化是模块化开发中不可缺少的,二者只不过是划分方式不同,实现方面没有太大区别。

2.2 组件或模块的单独运行

组件或模块的单独运行时组件化的又一亮点。还是以我们的demo为例,如果我们想单独运行module_home模块改怎么办?
首先你要知道一点组件变为一个能独立运行的app要变动那几个地方?我个人认为一般有三个地方需要变动:

  • 组件或模块的build.gradle 中的apply plugin: 'com.android.library' 变 apply plugin: 'com.android.application'
  • 组件或模块的build.gradle 中的defaultConfig配置中的applicationId 根据情况动态变动,如果作为组件或模块存在则不需要,反之亦然。
  • 功能清单文件的变动,你想如果作为组件或模块存在我们是不需要下面这些东西的:


    image.png

ok,那我们就依次解决这三个问题:
对于是否要将模块或组件单独运行,我们需要定义变量去控制,还是以demo为例,我们在project的gradle.properties文件中定义如下:


image.png

于是第一个问题解决了,我们只需要在模块的build.gradle中顶部添加如下代码:


image.png

于是第二个问题也解决了,我们只需要在defaultConfig中增加以下代码即可:


image.png

这里在提个醒默认情况下我们的applicationId值是我们的包名一致

于是第三个问题也解决了,我们还是在对应的build.gradle文件中的android中增加以下代码:


image.png

注意这个我们是要配置相关路径的,如:src/main/runalone/AndroidManifest.xml
那我们在这个位置就有相关文件夹,大家请看:


image.png

里面的内容如下(就是平常的啦):
单独运行的清单文件:


image.png

作为组件或模块的清单文件:


image.png

既然实现了组件的单独运行那么单独调试也就解决了。

2.3 数据的传递与ui跳转跳转

ui的跳转我们主要借助于阿里的Aroute,数据的传递可以Aroute和EventBus结合使用,效果更佳。
ARoute的介绍请参考我之前的文章Android-ARouter
还是以项目为例:

  • 跳转调用其它组件(这里主要是分享组件)
    比如我们要从首页模块调用x5组件,那么请看相关代码:
    首页相关代码,再点击跳转X5按钮后:

   @OnClick(R2.id.homemodule_button)
    public void onViewClicked() {
        ARouter.getInstance().build(COMPONENT_X5).withString("url","https://www.baidu.com/").navigation();
    }

x5分享组件相关代码:


image.png

数据的回调可以结合EvnetBus,这里就不详细说了。

  • 利用IProvider跨moudle的服务调用,主要用于非Activity,因为我在写demo中发现在Activity中不好使。直接上代码了:
    应用场景是我们zhongmodule_zixun模块中的Fragment要调用X5组件中的X5Test类中需要用到的方法:
  1. 首先在common_lib中定义X5CompService接口继承IProvider接口,如下:


    image.png

2.x5组件中的X5Test类实现X5CompService接口


image.png

3.在zhongmodule_zixun模块中的Fragment中获取X5CompService实例进行调用,进行了简单的toast


image.png

注意:既然是zhongmodule_zixun模块中的Fragment中获取X5CompService的实现类进行调用,那么它就要把X5组件作为依赖:


image.png

2.4 生命周期管理

生命周期的管理我们主要是通过common_lib中的baseApplication(注:其他module中的Application都要继承baseApplication,确保唯一性)来管理,这里我直接贴出BaseApplication中的所有代码:

public class BaseApplication extends Application {
    //是否开启调试
    private  boolean isDebug =true;
    //全局唯一的context
    private static BaseApplication application;
    //Activity管理器
    private ActivityManage activityManage;

    @Override
    public void onCreate() {
        super.onCreate();
        application = this;
        activityManage = new ActivityManage();

        //初始化路由
        initRouter();
    }

    /**
     * 程序终止的时候执行
     */
    @Override
    public void onTerminate() {
        super.onTerminate();
        exitApp();
    }

    /**
     * 退出应用
     */
    public void exitApp() {
        activityManage.finishAll();
        android.os.Process.killProcess(android.os.Process.myPid());
        System.exit(0);
    }

    /**
     * 初始化路由
     */
    private void initRouter() {
        //必须在初始化之前写入这两行
        if (isDebug) {
            //打印日志
            ARouter.openLog();
            //开始调试
            ARouter.openDebug();
        }
        //ARouter的实例化
        ARouter.init(this);
    }


    /**
     * 获取全局唯一上下文
     *
     * @return BaseApplication
     */
    public static BaseApplication getApplication() {
        return application;
    }


    /**
     * 返回Activity管理器
     */
    public ActivityManage getActivityManage() {
        if (activityManage == null) {
            activityManage = new ActivityManage();
        }
        return activityManage;
    }
}

ActivityManage使我写的Activity管理工具类,详情请看相关代码。

2.5 其他

  • 混淆
    混淆我们是放在各个Module还是app的proguard-rules.pro文件中,答案是app的proguard-rules.pro文件中,因为如果在组件中进行混淆,一旦代码出现了bug,这个时候就很难根据日志去追踪bug产生的原因,而且不同组件分别进行混淆非常不方便维护和修改。
  • 使用ButterKnife遇到的问题
    当我将组件单独运行时是没有问题的,可如果作为module时就会出现什么需要常量等问题,解决办法是:


    image.png

    将原本的R.id.homemodule_button改为R2.id.homemodule_button,如果找不到R2,请确保你project的build.gradle中:

image.png

但依赖的butterknife版本是8.4.0:


image.png

每一个用到的模块中的build.gradle中都需要配置(默认模块都依赖common_lib,否则单独添加8.4.0依赖):
apply plugin: 'com.jakewharton.butterknife' 以及


image.png

就这么多吧,详情请看组件化Demo.

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