随着软件开发复杂度的不断提高,如何能在不断变化的需求中快速适应和保证软件的质量显得尤其的重要,持续集成正是针对这一类问题的一种软件开发实践。文中的Jenkins-ci
容器化方案能够在项目构建过程中提取出更多自定义的需求信息并进一步将持续集成的环境参数配置标准化,通过Docker
的形式实现快速部署。
在产品的实际开发中,可能会遇到一些问题。例如要做一个app
产品,大致流程如图所示:
图中的代码检查和测试这部分,过程重复固定,比较费时费力,如果能够通过自动化构减少大部分这类工作那是最好不过了。
还有一些用户可能希望在产品迭代过程中,能够实时了解产品迭代的相关信息。例如某天新增了什么功能、修复了jira
上的哪些问题,某一次代码的提交对整个Apk
产生的影响,如方法数和大小等等。
针对上述的一些问题,我们借助于持续集成框架Jenkins
,能够很好的解决这些问题,并且进一步将Jenkins
配置环境容器化,实现快速配置。
也许有人会问,公司内部已经有Jenkins
持续集成这类技术支持了,为啥还要做呢,那肯定是我们在做的东西他们还没有呀,比如我们对自动化构建的内容进行了各种定制(包括Android Lint
、FindBugs
、FireLine
、Apk
包大小和方法数统计、资源文件统计,以及Git commit
日志分析),并提供可视化的构建报告等。
我们通过在Jenkins
中加入一些自定义的构建脚本、任务等,使得项目在自动构建过程中能够收集、整理、统计相关数据,并以报表的形式呈现给用户。配置完成的Jenkins
运行之后主界面如图,只需要进行简单的配置即可,不用修改项目,也不会对项目产生影响。
并且,Jenkins-ci
容器化方案支持两类构建通知:
编译报错通知和每日的工程构建报告都会通过邮件的形式发送,其中,构建报告的内容主要包括基础构建信息、代码分析、资源文件分析、打包产物Apk
分析、Git
日志分析和构建日志信息六个部分,如图:
Jenkins-ci
容器化方案主要包含自动化构建内容定制和结合Docker
实现快速配置两个部分内容。
本次自动化构建将包括Android Lint
、FindBugs
、FireLine
、Apk
包大小和方法数统计、资源文件统计,以及Git commit
日志分析几个部分。下面,将对这些自动构建任务进行一一讲解。
这里需要提到的一点就是,上述的这些构建任务会在Jekins
环境中通过脚本的形式动态添加并执行,不需要用户干预,避免修改项目代码。
Android Lint是一个静态代码分析工具,也是Android Studio
默认的代码检查工具。
它默认的检查规则有很多,常见的有6大类:
Correctness
)
ScrollViews
控件有且仅有一个子视图Toast
对象但没有调用show()
方法的代码Security
)
Activity
的exported
为true
时,设置一个Permission
,让使用者获取了Permission
才能使用JavaScript
就不要执行SetJavaScriptEnabled()
方法Usability
)Performance
)Accessibility
)I18n
)
限于篇幅,这里只做简单介绍,想要了解更多详细的Android Lint
规则,可参考lint-checks文档。
一般情况下,默认的这些规则已经足以满足开发的需求,但是在某些情况下,如默认的Android Lint
规则可能无法满足团队特定需求、存在一些检测缺陷或者缺少一些我们认为有必要的检测等等,可能还需要我们自定义Lint
规则。
关于如何自定义Lint
规则,可参考ht-lint项目。它是一个自定义Android Lint
规则的库,对Android Lint
规则进行了扩展,在不影响Android Lint
原有的检查项目的基础上进行额外内容检查。示例规则如图:
目前ht-lint
库定义的规则有2类:
Log
,Toast
,Handler
throw
抛出异常的时候需要把已经捕获的异常一并带上Message
必须采用Message.Obtain()
方法获取For/If/Try
最大深度是3Activity
或Fragment
对应的布局文件,命名要带上对应前缀。如activity_mediapicker.xml
、fragment_tab.xml
View-->v
,Button-->btn
,TextView-->tv
等等viewholder
关联的布局资源以item
为前缀。如item_shopping_cart.xml
构建项目时,执行gradle
任务lint
后,可以通过Jenkins
的插件Android Lint Plugin
来收集、整理以及显示lint
的检查结果。我们在通知邮件中仅显示摘要信息:
点击邮件中的链接地址,会跳转到Lint Issues
包含图文描述的界面,如图:
Findbugs是一个静态分析工具,通过一组缺陷模式与Java
字节码进行对比从而发现问题。
它定义的缺陷模式也有很多种,常见的有以下:
Bad practice
)
equals()
与hashCode()
没有同时定义,或者使用了错误的对象的hashCode()
或equals()
Multithreaded correctness
)
notify()
而不是notifyAll()
,只是唤醒一个线程而不是所有等待的线程Dodgy
)Malicious code vulnerability
)Internationalization
)Correctness
)Performance
)
这里只做简单介绍,更多缺陷模式的定义可参考FindBugs Bug Descriptions。FindBugs
也是支持自定义规则的,这里就不做过多解释了。
我们可以发现,这些缺陷模式部分内容与Android Lint
是有重复的,但更多的内容是其独有的,因此Jenkins-ci
也对FindBugs
进行了集成,提高代码检查质量。
FindBugs
需要通过apply plugin: 'findbugs'
的形式引入findbugs
插件,并自定义一个任务如findbugs
:
//findbugs插件
apply plugin: 'findbugs'
task findbugs(type: FindBugs, dependsOn: 'assembleDebug') {//依赖Debug打包所产生的class文件
...
excludeFilter = file("${project.rootDir}/configs/scripts/findbugs-filter.xml") //配置过滤文件,减少不必要的检查
classes = files("${project.buildDir}/intermediates/classes/")//默认分析的class文件对象
...
}
执行findbugs
任务之后,通过FindBugs Plugin
可以获取检测结果并显示。最后,在邮件中显示检查的摘要信息如图:
点击邮件内链接地址,同样跳转到FindBugs
检查结果的图文详情界面。
火线(FireLine
)提供一种静态代码扫描服务,基于PMD
开源。FireLine
的检查规则不是很多,目前只是包括安全、日志、内存和基础四类规则:
Activity
组件导出会导致拒绝服务AndroidMannifest.xml
文件中allowBackup
设置为true
时会导致数据泄露Log
中不要输出敏感信息,例如pid
、uid
、imei
号等Log
方法中对变量进行赋值操作Stream
对象关闭失败,打开的资源对象需在finally
中关闭Stream
对象因异常未关闭,打开的资源对象需在finally
中关闭finally
块中返回,这会导致异常捕获后又被抛弃
FireLine
的许多检查规则看上去更像是项目最佳实践规则,与Android Lint
、FindBugs
相互补充,不断完善。因此在自动构建中进行了集成。
FireLine
对外提供一个jar
包,必须通过命令行的形式运行,因此需要添加如下的gradle
任务:
//扫描java源码,需指定扫描对象以及结果存储位置
task fireLine << {
def fireLineDir=env.JENKINS_HOME+"/jobs/"+env.JOB_NAME+"/builds/"+env.BUILD_NUMBER+"/"
exec {
workingDir './'
//命令行执行jar包
commandLine "java", "-jar", "${project.rootDir}/configs/jars/fireline.jar", "scanSrcDir="+env.WORKSPACE, "reportSaveDir="+fireLineDir,"reportFileName=fireLineResult","user=netease"
}
...
}
不过可惜的时,Jenkins
中还没有专门的插件来显示FireLine
的检查结果,因此要显示fireLine
生成的静态html
页面还需要通过插件HTML Publisher Plugin
进行支持。
构建后,FireLine
检查结果在邮件中显示如图:
点击链接地址,跳转到FireLine
的详细报告界面:
随着项目的不断迭代更新,android
应用不得不面对64k
方法数限制、Apk
体积不断变大的问题,并且这类数据人工统计也不方便。方法数限制一般是由于自身代码逻辑太复杂或者引入的第三方库导致的,而Apk
的过大主要由so
文件和资源文件导致,如涉及Android
屏幕适配,资源文件就特别多。因此,本次实践中将对该类数据进行收集分析,更加直观的展现给关注该类信息的用户。
在Apk
方法数统计集成中,采用了开源的Gradle
插件项目-dexcount-gradle-plugin,该插件会根据配置为打包的每个Apk
文件生成一份方法数和字段统计的文件:
buildscript {
repositories {
mavenCentral() // or jcenter()
}
dependencies {
classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.6.1'
}
}
// make sure this line comes *after* you apply the Android plugin
apply plugin: 'com.getkeepsafe.dexcount'
配置完成后,执行assemble
任务时,会在输出目录下生成apk
方法数统计数据文件:
最后,为了在jenkins
中使用该数据,创建一个apkMethodCounts
任务将上述的所有的json
格式文件进行统一解析转换,并将解析结果和生成的Apk
文件--对应起来。构建结果在邮件中显示如图:
资源文件统计插件resource-size-plugin与dexcount-gradle-plugin
的使用方法基本一致,该插件也是需要通过动态添加。
插件配置完成后,执行resourcesize
任务,会在输出目录下生成分析结果resoucesize.txt
。其中,第一行表示所有资源文件的总大小,后面每行代表单个文件最大的文件名称以及文件大小:
最后,将数据进行简单处理,构建结果在邮件中显示如图:
随着项目的迭代,代码的提交越来越频繁,代码的管理的重要性越来越突出,代码提交的规范性越来越受到重视。在没有规范的情况下,提交的日志信息可能如图:
尽管日志对项目的正常推进不会构成太大影响,但也带来了一些不可忽视的问题:
因此,在参考了一些Git
规范的基础上,整理出一份可行的Git提交规范。其规定了完整的git
提交日志由信息头部、信息主体和信息尾部构成,其中信息头部需要包含类型、范围和主题三类信息。
<type>(<scope>):<subject>
空行
<body>
空行
<footer>
Fix/Close #bug号,#bug号,...
从长远来看,日志的规范性是很有必要的,它不使得日志信息更加容易检索、阅读,而且日志信息的可利用性也大大提高。我们完全可以针对日志中的某些类信息进行提取转化,进行有效利用。
因此,在本次集成中,我们就对提交日志中的feat
、bug fix
两类信息进行了收集处理。
Git
提交规范随便不复杂,但是在编写的过程中可能一个不留神就会导致日志不够规范。因此,这里也给大家推荐一个撰写合格日志的工具--Commitizen,很好用,推荐~。使用该工提交代码之后,会自动生成上述格式规范的日志信息。
通过Commitizen
工具(或者手动)提交之后,生成的Commit message
如图:
从图中的示例可以看出,被处理的日志信息的类型是feature
和bug fix
的两类。在构建的过程中提取出这些日志信息后,进过简单的整理、转化,就可以展示给用户。

到此,关于自动化构建内容定制的部分已经介绍完了~后续可能继续对定制内容进行扩展,敬请期待~
上一节仔细讲解了自动化构建内容定制,其中涉及到很多的插件、工具,一个一个配置起来很麻烦,而且配置过程中还有很多坑。不过大家不要慌,我们已经为大家考虑到这点了,提供了解决方法,那就是使用Docker容器。基于Docker
轻量级、可移植的特点,本次实践将Jenkins-ci
的构建环境配置打包到Docker
中,从而实现快速配置。
Jenkins
集成镜像的内容主要有4个部分:
目前,该镜像已经上传到网易云计算基础服务
可通过一下命令获取:
docker pull hub.c.163.com/netease163/ht-jenkinsci:latest
更多内容可参考网易云计算基础服务使用指南。
网易云新用户大礼包:https://www.163yun.com/gift
本文来自网易实践者社区,经作者付光鑫授权发布。