最是人间留不住,朱颜辞镜花辞树。这篇文章主要讲述Gradle自动实现Android组件化模块构建相关的知识,希望能为你提供帮助。
文章图片
背景随着App的不断迭代,业务会变得越来越复杂,业务模块会越来越多,且每个模块的代码也会变得越来越多。为了应对这一场景,我们需要把不同的业务模块划分成一个个组件,在修改业务代码的时候只需要在对应模块修改就可以了。通过高内聚,低耦合的业务模块来保证工程的健壮性和稳定性。现在问题来了,当组件的数量变得越来多的时候,我们如何管理业务组件呢?
原创声明: 该文章为原创文章,未经博主同意严禁转载。为什么我们要用Gradle管理组件呢?先来看看android组件化需要实现的目标。(什么是组件化构建?)
- 按照业务逻辑划分模块
- 项目模块能够单独启动测试
- 能够根据需求引入或删除某些业务模块
- 通过不同模块的组合,组成不同的App
对于第二点:Android是通过应用com.android.application或com.android.library来决定该模块是以App模式还是以Library模式构建。App模式和Library模式的最大区别就是,App能够启动,而Library不可以。所以如果我们的模块能独立启动的话,我们需要每次手动去改动模块的build.gradle文件。好一点的做法定义一个布尔值来判断是否处于debug模式,但是这里有个问题是,不是每个模块都能独立启动的。所以无论采用何种方案,都需要我们手动管理。
对于第三点:当我们开发好业务模块后,可能我们需要频繁的新增或删除某些业务模块。如果是这样的话,我们也是需要频繁手动修改App的build.gradle。
对于第四点:有时候,我们可能会在不同的App中引用相同的组件(例如:滴滴的普通版和企业版,普通版包含企业版的功能),这个时候,我们也不希望要频繁手动管理组件依赖,特别是在组件还可以独立运行的时候。
所以,在我们实践组件化的时候,最大的问题就是,我们需要频繁的手动build.gradle文件来管理组件应用的插件和App的依赖。
使用Gradle来管理组件先安利下笔者写的Gradle插件:Calces。如果觉得这个插件有用的话,可以star下,如果你有更好的想法的话,可以向我提交pull request。
废话少说,以下是通过Calces快速实现Android组件化构建的流程。
你的支持,是我前进的动力,如果觉得有帮助的话,可以点下star??Demo地址:SimpleCalces
项目结构:
文章图片
- 引入依赖库
在项目的build.gradle中
buildscript {
...
}
plugins {
id "calces.modules" version "1.0.11"
}
在较旧版本的Gradle中或需要动态配置的情况下的插件构建脚本代码:
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "gradle.plugin.com.tangpj.tools:calces:1.0.11"
}
}
apply plugin: "calces.appConfig"
- 在项目build.gradle配置AppConfig
appConfig {
debugEnable falseapps {
app{
modules ‘:library1‘, ‘:library2‘
}
}modules{
library1{
mainActivity ".Library1Activity"
applicationId "com.tangpj.library1"
isRunAlone true
}
library2{
mainActivity ".Library2Activity"
applicationId "com.tangpj.library2"
isRunAlone true
}
}}
- 在modules(子模块)引入模块自动化构建插件 (注意:不需要手动配置com.android.library或com.android.application)
apply plugin: ‘calces.modules‘
这样我们就完成了组件化的构建了,是的,我们不再需要再手动管理单个组件了与App的构建了,通过Calces,我们能实现快速的组件化构建与多App同时构建。
那么问题来了,我们如何实现组件间的通信呢?在简单的项目中,推荐该Demo一样,通过使用Android隐式Intent来实现组件间通信。在中大型项目中,笔者推荐使用阿里的路由解决方案:ARouter。具体使用方法参考官方文档就可以了,使用方法十分简单。
注意:在使用隐式Intent实现组件件通信的时候需要注意找不到相应组件异常:java.lang.IllegalStateException: Could not execute method of the activity。导致这个异常的原因是找不到目标组件导致的,所以在实际开发的时候,需要捕获这一异常,并且根据项目实际情况来进行实际的处理。使用ARouter框架则能通过设置降级策略来实现异常处理(查看ARouter文档了解更多)。如果只是实现项目的简单组件化,那么看到这里就可以了,如果希望实现更加灵活的组件化架构的读者可以继续看下去,下面笔者将全面分析组件化的优势与笔者总结的组件化构建思想。
组件化构建简述组件化构建与其说是一种技术,不如说是一种思想。组件化构建是通过对项目重新划分成一个个高内聚、低耦合的模块来解决项目过于臃肿,代码过于复杂的一种架构思想。
我们通过对Google官方的架构演示Demo todo-mvp进行拆分来对Android组件化进行深入的分析。
Demo地址:TodoCalces
todo系列app是Google android-architecture项目中为了演示Android架构的最佳实现而编写的一系列演示Demo。todo app的特点是,它足够简单,代码量少,易于理解。但是又不会过于简单,因为它是一个包含完成功能的App。它实现了任务列表、任务详情、新建任务、编辑任务、统计任务的功能。todo-mvp实现的功能:
- 任务列表
- 任务详情
- 新增/编辑任务
- 统计任务
架构划分图如下:
文章图片
从架构图可以看出,所有的业务组件都依赖底层库,而APP又依赖于业务组件,APP组件在这里是作为一个独立组件存在的。在一般的组件化实践中,都不包含APP这个组件的,APP组件的存在是有其意义的。
首先,我们的组件化除了实现组件的独立管理和动态配置APP所依赖的组件外,还有一个十分重要的目的就是,通过组合不同的组件,打包多个不同的APP。例如,QQ有分普通版和轻聊版,轻聊版是功能简化版的QQ。如果我们使用组件化来管理工程的话,我们只需要把不需要的模块移除掉就可以了。而APP组件在这里的作用是充当一个包装盒,把需要的组件包装进来。并且我们可以通过控制包装盒的样式来配置不同的APP风格。在这里我们可以通过Application中的Style来实现。
这里我们还是以todo-mvp为例,例如我们需要实现一个不包含统计功能的todo APP,按照我们的原理,我们只需要去掉statistics的依赖就可以了。
架构划分图如下:
文章图片
如果nostatsitcs需要不同的配色的方案的话,只需要在AndroidManifest的application标签中配置对应的theme就可以了。
使用Calces实现todo-mvp的组件化通过上面的分析,我们来试下对todo-mvp项目按照业务功能来划分组件。我们先来看看划分后的目录:
文章图片
好了,我们已经对todo-mvp项目进行初步的划分了。根据上面分析的理论得知,我们的业务模块是可以单独运行的,并且我们能够快速构建一个不包含statistics模块的APP。
我们只需要使用Calces就能快速实现我们需要的功能。
按照Calces的教程,我们得知,实现Calces只需要三个步骤:
- 引入依赖库
- 在项目的build.gradle中配置AppConfig
- 在业务模块中引入模块自动化构c持续
appConfig {debugEnable falseapps {
app {
mainActivity "com.tangpj.tasks.TasksActivity"
modules ‘:modules:addtask‘,
‘:modules:taskdetail‘,
‘:modules:tasks‘,
‘:modules:statistics‘
}app2 {
name ‘nostatistic‘
applicationId ‘com.tangpj.nostatistic‘
modules ‘:modules:addtask‘,
‘:modules:taskdetail‘,
‘:modules:tasks‘
}}modules {
addtask {
name ":modules:addtask"
applicationId "com.tangpj.addtask"
mainActivity ".AddEditTaskActivity"
isRunAlone false
}taskdetail {
name ":modules:taskdetail"
applicationId "com.tangpj.taskdetail"
mainActivity ".TaskDetailActivity"
isRunAlone true
}task {
name ":modules:tasks"
applicationId "com.tangpj.tasks"
mainActivity ".TasksActivity"
isRunAlone true
}statistics {
name ":modules:statistics"
applicationId "com.tangpj.statistics"
mainActivity ".StatisticsActivity"
isRunAlone true
}}
}
根据AppConfig可以得出,我们分别配置了2个APP,分别是app1和app2。并且app2中是没有依赖statistics的。现在我们两个APP运行的对比图。
app1(带statistics模块):
文章图片
【Gradle自动实现Android组件化模块构建】app2(不带statistics模块):
文章图片
从运行图可以看出,app1和app2的配色方案是不一样的,并且app2中不带statistics模块,通过对项目实行合理的划分和引入Calces就能够快速实现组件化构建了。
结论:通过Calces能轻松实现业务组件的管理,多APP的快速构建。当我们的业务组件只有4个的时候,可能无法体现Calces的优势,但是如果我们的业务组件有40个的时候,Calces给我们带来的优势就非常明显了。我们可以通过灵活依赖不同的组件,实现快速构建多个APP的目的。就像Calces的介绍图案一样,把组件当成积木来使用。
如何测试Android自动化测试展开来说是一个非常大并且不算简单的工程,在这里笔者不打算展开来说。只是简单的介绍组件化构建如何让我们更方便地去测试。
并不是所有的自动化测试都一样,它们通常在使用范围、实现难度和执行时间上存在不同。我们一般把自动化测试划分为三种分别是:
- 单元测试:目的是测试代码的最小单元。在基于Java的项目中,这个单元是一个方法。单元测试容易编写,快速执行,并在开发过程中针对代码的正确性提供宝贵的反馈。
- 集成测试:用来测试一个完成的组件或子系统,确保多个类之间的交互是否按预期运行。集成测试需要比单元测试需要更长的执行时间,而且更加难以维护,失败的原因难以诊断。
- 功能测试:通常用于测试应用程序端到端的功能,包括从用户的角度与所有外部系统的交互。当我们讨论用户角度时,通常是指用户界面。因为用户界面会随着时间的推移发生变动,维护功能测试代码会变得乏味而耗时。
占比如下图所示:
文章图片
从上文知道,在我们的组件化分的时候,会划分一个基础依赖库(superLib)。基础依赖库为我们的项目提供了基本的支持,并且该库在项目中是比较稳定、并且不包含业务逻辑的,所以在基础依赖库中,我们应该大量应用单元测试。而集成测试则适用于我们的数据依赖库(dataLib)中,我们可以通过集成测试来验证产品代码与数据模块的交互。而我们的业务模块中包含了大量的业务逻辑,这部分是经常变动的部分,我们可以为我们的业务模块编写一些UI自动化测试代码,但是因为业务(界面)经常变动的原因,所以这部分测试代码是难以维护,并且复用性十分低的。。
最后,我们得出的结论是:应该把主要精力放在单元测试上,所以如果当你的精力不足以编写所有测试代码的时候,你应该把主要的精力放在单元测试上,而不是放在收益最小的功能测试上。
关于自动化测试,笔者给的建议就到这里了,如果需要深入理解测试的话,可以自行查找资料,或者关注笔者的博客。后续的博客中,有可能会写关于自动化测试相关的知识。
小结通过Calces插件,我们在实现Android组件化时只需要关注如何合理划分组件的架构与如何实现组件间的通信就可以了。对于Android组件化来说,最主要问题有两个:
- 大型项目如何合理划分组件模块
- 当项目的组件数量非常多的时候如何管理
历史精选Android开发利器之Data Binding Compiler V2 —— 搭建Android MVVM完全体的基础
关于我如果这片文章对你有所启发的话,可以关注下笔者的公众号或GitHub。
扫一扫关注我:
文章图片
推荐阅读
- Appium 1.9.1 启动报错 Fatal TypeError: Class constructor BaseDriver cannot be invoked without 'new&#
- android完成注册页面的下拉框及单复选框
- 9Collaborative Metric Learning Recommendation System:Application to Theatrical Movie Releases----
- Jmeter入门13jmeter发送application/octet-stream二进制流数据
- Spring MVC - 02 RequestMapping映射请求
- 收集安卓手机在市场占比的网址查找
- ABAP的Package interface, 安卓的manifest.xml和Kubernetes的Capabilities
- 与安卓交互
- ngs中reads mapping-pku的生信课程