一次 Android 项目组件化

原文链接:https://blog.lujun.co/2017/02/15/android-to-modularity/

组件化?

模块化

曾经也许你做过这样的事,将一些公共的代码组成独立的 module 并编译为 library 供各个子模块或其他项目引用,通常利用这种方式实现项目模块化(小功能独立拆分)。

组件化

那么什么是组件化?概括为:组件化是基于可重用目的将项目按照具体业务需求进行拆分,并能将拆分得到的组件进行灵活重组,减小耦合(业务需求上的拆分)。

模块化与组件化

组件化和模块化,两者都是为了实现解耦/重用而拆分项目为多个模块;相对于模块化,组件化的拆分粒度更大。

移动应用中项目组件化的目的就是让若干业务 module 能够并行开发,每一个业务 module 都能生产与之对应的 library 与 App,最终能够根据具体的需求灵活的组织不同的业务 module 生产主 App。对于 Android 项目中组件化的概念,可以看看这个 PPT

为什么组件化?

  1. 公共代码/业务代码解耦
  2. 共享模块
  3. 组件 module 代码的控制/编写/修改能保证不相干组件 module 代码的稳定性(不需要修改其他组件的业务,避免产生相关干扰,还能提高测试效率)
  4. 减少具体业务 module 的编译时间,提高开发效率(Android 编译...)

Android 项目组件化

组件化结构

Android 中组件化主要是通过脚本来控制,接下来以 Android Studio 中 Gradle 脚本为例来实现简单的一个项目组件化。这里不会讲解如何对主体业务进行组件化的拆分,因为不同的业务在不同的需求下都能产生一套合适自身的拆分规则。

组件化首先要保证不同的业务 module 既能够单独的生产、调试,也能够灵活的作为一个业务分支组建到主 App 中。我们知道,通过控制 gradle 脚本中的apply plugin: 'com.android.xxx'可以实现 Android module 运行于不同的状态,常用做法是自定义一个变量去控制当前业务 module 所处的状态。脚本如下:

if (isDevModel.toBoolean()) {
    apply plugin: 'com.android.application'
}else{
    apply plugin: 'com.android.library'
}

其中isDevModel定义在主项目 gradle.properties 文件中(当然也可以定义在子业务 Modue 中)。对于基础组件,比如通用的工具类库,按照模块化思路作为一个 library 就行。

注意:在 Android 开发中当一个 module 需要有applicationlibrary形态时,它们分别对应的AndroidManifest.xml文件是不一样的。所以需要为当前 module 提供两套AndroidManifest.xml文件,再编写脚本根据当前 module 需要处于的形态自动去选择编译对应的文件,脚本如下:

android {
    // ...
    sourceSets {
        main {
            if (isDevModel.toBoolean()) {
                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/release/AndroidManifest.xml'
            }
        }
    }
}

项目组件化架构图大致如下所示:

Android_to_modularity_architecture.png

项目组件化后的结构图大致如下所示:

android_modularity_project_tree.png

其中,app module 是主 module;businessmodulebusinessmodule2是业务 module,可以单独作为一个 Application 运行或作为主 module 的依赖组建到其中运行;baselibrary作为基础组件 library 供其他任何 module 依赖使用。

组件化中 Module 组建

Module 之间互相依赖以便于组建满足需求的 App。举一个例子:现在需要发布 release 版本的 App,需要将 businessmodule1 业务组件发布出去,此时只需要将isDevModel修改为false,并在主 moduleapp中依赖businessmodule1module即可。依赖脚本如下:

dependencies {
    // ...
    if (!isDevModel.toBoolean()){
        releaseCompile project(':businessmodule1')
    }
}

其中,businessmodule1module 又是依赖基础组件baselibrarymodule 的:

dependencies {
    // ...
    compile project(':baselibrary')
}

当开发者单独对businessmodulemodule 进行业务修改时,再将isDevModel修改为true,单独运行这一个 module 即可。

因为单个业务组件 module 的依赖相对不会太多,从而使得编译时间大大减少。

组件化遇到的问题以及一些小技巧

业务组件之间通信

不同的组件 module 之间 Activity 跳转

  • 类名跳转(看这篇文章介绍),不方便携带数据
  • 显示换隐式,定义 schema 跳转(外链跳转 App),可携带少量数据

Module 间通信也可采用事件总线进行

  • EventBus/Otto/自定义 RxBus

不同环境业务组件使用不同环境的基础组件(解决 CI 中涉及到的自动化问题)

举个例子,比如基础组件 module 是一个网络请求库,其中涉及到了测试环境和生产环境使用不同的代码,业务组件 module 也需要根据不同的情况区别使用该网络请求库生成的 library。

基础组件 module 中根据不同的环境使用不同的代码。这里我们为baselibrarymodule 的 debug 和 release 模式分别准备一套代码,整体 module 结构图如下所示:

android_modularity_lib_multi_varaint.png

如上图所示,debug 和 release 中都有一个包名和类名相同的Name类,但其中的代码是不一样的。具体可以查看源码。为了能够在 debug 和 release 模式下分别使用设置的代码,需要使用sourceSets控制代码的合并,如下:

android {
    // ...
    sourceSets {
        debug {
            java.srcDirs = ['src/main/java', 'src/debug/java']
        }
        release {
            java.srcDirs = ['src/main/java', 'src/release/java']
        }
    }
}

此外在发布该 library 时,需要指定一些设置,如下:

android {
    // ...
    defaultConfig {
        // ...
        defaultPublishConfig 'release'
        publishNonDefault true
    }
}

说明:

  • defaultPublishConfig 'release',默认 library 只会生产 release 下的版本,此版本将会被所有项目使用,通过defaultPublishConfig可以控制默认生产哪个版本的库。
  • publishNonDefault true,默认情况下不能生产所有版本的 library,通过设置publishNonDefaulttrue,可以同时生产所有版本的 library。

业务组件 module 依赖不同的基础组件生产的 library,如下:

dependencies {
    // ...
    debugCompile project(path: ':baselibrary', configuration: "debug")
    releaseCompile project(path: ':baselibrary', configuration: "release")
}

在使用 CI 时,通过这样的配置脚本解决了多个 APK 包依赖同一组件生产的不同的 library,最终得到我们需要的开发/测试/生产 APK 包。

其他问题

  1. 资源名称冲突,可以添加resourcePrefix解决。注意当添加了该属性后,所有资源名称必须用此 prefix 开头,否则作为 library 时会报错。
  2. Application 冲突,通过设置exclude将业务组件 module 对应的 Application 文件隔离。具体参照 ModularityDemo

总结

解耦、独立业务模块开发(减少编译时间),这些都应该是项目组件化的理由。同时,对一个既有项目实施组件化,考虑足够是最高效的方式。

源码

源码

参考

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,457评论 25 707
  • 今天来回味下组件化和模块化,这2种说法时一回事,当然还是有区别的,下面再详细说,其实很简单,只是设计范围的不同,也...
    前行的乌龟阅读 48,465评论 6 94
  • 不怕跌倒,所以飞翔 组件化开发 参考资源 Android组件化方案 为什么要组件化开发 解决问题 实际业务变化非常...
    笔墨Android阅读 2,968评论 0 0
  • 感谢天父爸爸今天让我休息一天。感谢主让我没有扔掉热水壶,如果扔掉热水壶我的脚会烫着更严重,感恩干弟兄夫妻两的关心,...
    梅英_bae7阅读 183评论 0 0
  • 我们每天都要和不同的人打交道,交流不可避免,有的时候有些交流真是让你无语,可有些懂你人的交流真是很妙的感觉。 一次...
    珍珠琉璃阅读 331评论 0 0