全自动化的 Android 编译管线
【编者按】Nicolas Frankel 是 hybris 的高级顾问, 在Java / J2EE 领域拥有超过10年的管理经验,本文阐述了他在使用自动化工序去构建 Android 应用程序遇到的一些难题,大家不妨读读,希望能有所收获。
以下系译文:
在我目前的工作中,我必须使用一些自动化工序去构建 Android 应用程序。这篇文章的目的就是描述我所遇到的难题,避免读者在这个过程浪费更多时间。目的就是分享我在这一过程中所遇到的困难,为读者提供前车之鉴,从而节省宝贵的时间。
环境搭建如下:
- 使用 Puppet 搭建基础设施
- 使用 Jenkins 搭建 CI 服务器
- 工程文件
- 来构建主体
- 作为主要测试工具
Puppet and Jenkins
事实上,我的准备工作已经相当完备。同事们已经使 Jenkins 服务器可以自动安装,以及准备好了所需的软件包——包括 Java 和已提供的可复用的 Puppet 类。Jenkins 的工作完全依赖于一个单一 config.xm
文件,即不同部分的封装。每部分都由一个专门的模板处理。因此,在我看来,创建一个简单的 Gradle 任务就如同在公园里散步一般轻松,最多几天时间便可以完成。
第一步非常容易:只需一个最新版 Puppet 清单,能帮助你添加 Gradle 插件到 Jenkins 服务器。
The Gradle wrapper
如果你是我博客的忠实读者,那你大概知道我对 Gradle 的看法。不过,我必须得承认,Maven 的确缺乏这种兼容性,即不论安装哪个版本的工具都确保编译成功——虽然它应该具备该功能。为了实现这一目标,Gradle 通过提供一个 JAR、一个 shell 脚本和一个属性文件,属性文件还包含了从 URL 到 Gradle ZIP 的分发,组装成所谓的包装机制。这三个需求都被存储在 SCM 中。
然而这正是麻烦的开始。在一个企业环境中进行下载,意味着要通过和验证代理。最简单的选择莫过于在工作配置下设置好一切,包括代理凭证。然而,从安全的角度来看,这样的做法并不理想,因为任何人访问 Jenkins 接口或文件系统,都能够读取这些凭据。显然,我们需要一个更好的方式。
用户已经拥有了配置代理完备的 Nexus 库。上传所需的 Gradle 分布,并更新指向它的 gradle.properties,简直易如反掌。
The Android SDK
Android SDK 只是一个 ZIP 文件。我用同样的方法:先下载文件然后将其上传到 Nexus。这一步之后,一个 Puppet 脚本会负责下载、提取,并为它设置正确的权限。
然而,事情并没有想象的那么简单。Android 开发者都知道,Android SDK 需要手动操作:开发者必须手动检查所需平台和工具,并将其下载在本地文件系统。这看上去很简单不是吗,但如果转为自动操作则会让很多开发者头疼,尽管有一个命令行相当于可以通过 SDK「带有 --no-ui
参数」来安装/更新包。如果你想了解更多,请点击这里。
谷歌工程师未能提供的两个重要参数:
- Proxy credentials – login/password
- Accepting license agreements
为了解决这一问题,网上有很多蹩脚的方案,最诱人的应该要数配置文件了,但我却发现它们没多大用。然而,通过 expect
命令的使用,我反而发现了一种创造性的解决办法。Expect 是一个漂亮的命令,用来读取标准输出,并用标准输入进行相应的填写。值得一提的是,它竟然还可以接受正则表达式。所以,在请求代理登录时,你键入登录名、填写密码,当它要求许可证接受时,你键入「同意」就能轻松搞定。虽然我反复多次试验,历经很多错误,才达到预期结果。但这个方法非常简单、直接。
我最初的设计是,使所有可能用到的装有 Puppet 的 Android 包,成为服务器配置的一部分。在标准化操作中「如文件创建或系统包安装」,Puppet 可以确定这项配置是否必要。例如:如果某文件已经存在,那就没有必要再重复创建了。在最后的 Log 中,Puppet 会报告它执行的每一个操作。起初,我试图通过在配置过程中,人为地告诉 Puppet 哪个包是已配置的,因为 Android SDK 为每个包都创建一个文件夹。但要命的是,Puppet 只接受单一文件夹来验证。对于某些包来说,并没有任何版本信息「例如 Google 游戏服务」。
因此,一个同事提出将 Puppet 配置的更新,移动到每个任务的预先步骤中。这样能修复非幂等问题,同时,还能在每个任务中更新所需配置。
Robolectric
说到这里,我本以为一切都搞定了。但非常不幸,并不是这么回事,就因为这个库—— Robolectric。
此前,我对 Robolectric 没什么了解,只知道这是一个测试库,能够在 Android 上运行测试,而无需任何物理设备的连接。在 Jenkins 上试图编译时,我偶然发现了一个「有意思」的问题:尽管 Roboletric 提供了一个具有完整依赖性的 POM, 但 MavenDependencyResolver
类硬编码库应该从哪里下载?
唯一的解决办法是通过扩展上面的类来实现。我用的就是上面提到的企业 Nexus 库。
上传并发布任务
为了实现前面的任务,我只需添加一个自定义 Gradle 任务,从 settings.xml
得到 Nexus 设置「由 Puppet 调配」。基于此,我成功地上传了任务。最后,对于每个任务执行的型号,我添加到所上传的工件集的输出文件中。因此,不管编译文件是哪种型号配置,下面的命令都将只上传 XXX 和 YYY:
./gradlew assembleXXX assembleYYY upload
上面的任务都搞定了,那发布岂不是更简单:唯一需要的就是设置 Gradle 插件,它添加了一个发布任务,类似于 Maven 的 deploy。
结束语
作为后端开发人员,我已经习惯于持续集成设置,毫无疑问,我可以在几天内搞定 Android CI 的进程。对于 Android 系统在 CI 上的欠缺,我觉得不可思议。每一步都苦不堪言,糟糕的记录「如果有的话」和解决方案似乎更像黑客般具有破坏性。如果你想沿着这条路走下去。呐,别说我没告诉你……Good Luck!
原文链接: Fully Automated Android Build
本文系 OneAPM 工程师编译整理。OneAPM 是应用性能管理领域的新兴领军企业,能帮助企业用户和开发者轻松实现:缓慢的程序代码和 SQL 语句的实时抓取。想阅读更多技术文章,请访问 OneAPM 官方博客。
全自动化的 Android 编译管线的更多相关文章
- 6.6 Android 编译机制的变迁
我们使用Java开发android,在编译打包APK文件时,会经过以下流程 Java编译器将应用中所有Java文件编译为class文件(JVM运行的是.class文件,而DVM是.dex文件) dx工 ...
- [Android 编译(一)] Ubuntu 16.04 LTS 成功编译 Android 6.0 源码教程
本文转载自:[Android 编译(一)] Ubuntu 16.04 LTS 成功编译 Android 6.0 源码教程 1 前言 经过3天奋战,终于在Ubuntu 16.04上把Android 6. ...
- 【Linux】CentOS下升级Python和Pip版本全自动化py脚本
[Linux]CentOS下升级Python和Pip版本全自动化py脚本 CentOS7.6自带py2.7和py3.6 想要安装其它版本的话就要自己重新下载和编译py其它版本并且配置环境,主要是软链接 ...
- Android编译优化系列-kapt篇
作者:字节跳动终端技术---王龙海 封光 兰军健 一.背景 本文是编译优化系列文章之 kapt 优化篇,后续还会有 build cache, kotlin, dex 优化等文章,敬请期待.本文由Cli ...
- Android编译环境折腾记
题记:感觉是时候写点什么了=_=! 第一次安装了ubuntu14.04.5,官网下载的iso,官网下的jar,编译android4.x需要安装jdk6,更高的版本会有问题,baidu到很多搭建环境的步 ...
- Cocos2d-x3.6 Android编译问题
在Cocod2d-x论坛上看到越来越多人吐槽新版本更新太快,改动太大,而且经常有BUG导致升级要折腾很久很久..但我就是喜欢折腾,喜欢升级到最新版本,看看有了哪些新功能,哪些改进.为此也折腾了不少,遇 ...
- Xamarin.Android编译CPU类型选择方式
Xamarin.Android编译CPU类型选择方式 在Xamarin.Android编译的时候,默认提供了5种CPU类型供大家选择.它们分别为armeabi.armeabi-v7a.arm64-v8 ...
- Xamarin.Android编译提示找不到mscorlib.dll.so文件
Xamarin.Android编译提示找不到mscorlib.dll.so文件 错误信息:AOT module ‘mscorlib.dll.so’ not found: Cannot load lib ...
- Android编译过程详解(一)
Android编译过程详解(一) 注:本文转载自Android编译过程详解(一):http://www.cnblogs.com/mr-raptor/archive/2012/06/07/2540359 ...
随机推荐
- <?php>慢慢写一些php的cookie问题<?>
写网站是个爬坑的过程,在你设计完功能之后,就会发现:卧槽,这个怎么实现?你妹,这个能实现么? 进了公司分工明确还好说(= =学长们都这么说),在学校自己没事写一些项目的话只能自己爬坑了. 蹬蹬瞪蹬,登 ...
- poj 2507Crossed ladders <计算几何>
链接:http://poj.org/problem?id=2507 题意:哪个直角三角形,一直角边重合, 斜边分别为 X, Y, 两斜边交点高为 C , 求重合的直角边长度~ 思路: 设两个三角形不重 ...
- afddaf
//import javax.swing.*; import javax.swing.JFrame; import javax.swing.JButton; import javax.swing.JL ...
- 如何实现GridView的选中,编辑,取消,删除功能
protected void GridView1_RowDeleting(object sender, GridViewDeleteEventArgs e) { string sqlstr = &qu ...
- [原创]PostgreSQL Plus Advanced Server批量创建分区表写入亿级别数据实例
当前情况:大表的数据量已接近2亿条我的解决思路:为它创建n*100个分区表,将各个分区表放在不同的tablespace上这样做的优点:1.首先是对这个级别的数据表的性能会有所提升2.数据管理更科学3. ...
- Linux编译内核提示'make menuconfig' requires the ncurses libraries错误
原来使用的ubuntu 11.10系统由于误操作,导致系统崩溃,重新安装了ubuntu 11.10: 在编译内核的时候,提示如下错误: dingq@wd-u1110:~/hwsvn/2sw/1prj_ ...
- 使用工厂bean和Utility Schema定义集合
工厂bean是实现了beanFactory接口的bean,也可以继承AbstractFactoryBean,主要是用于在给定属性参数之后自动创建一个bean对象. 我们在使用基本集合标记定义集合时,不 ...
- JVM学习总结五(番外)——JConsole
之前本来打算结合自己写的小程序来介绍JConsole和VirtualVM的使用的,但是发现很难通过一个程序把所有的场景都体现出来,所以还是决定用书中的典型小例子来讲更加清晰. 一.JConsole的基 ...
- metaq
MetaQ(全称Metamorphosis)是一个高性能.高可用.可扩展的分布式消息中间件,思路起源于LinkedIn的Kafka,但并不是Kafka的一个Copy.MetaQ具有消息存储顺序写.吞吐 ...
- 条款2:尽量以const、enum、inline替换#define
1> 以const替换#define • 比如用const double Ratio = 1.653替换#define RATIO 1.653 因为宏定义在预处理阶段就会被替换成其所指代的内容, ...