这里有新鲜出炉的精品教程,程序狗速度看过来!
Android 是一种基于 Linux 的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由 Google 公司和开放手机联盟领导及开发。尚未有统一中文名称,中国大陆地区较多人使用 "安卓" 或 "安致"。
本篇文章主要介绍了 Android 的自动化构建及发布,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
在一个 App 从开发到测试的过程中,我有很长一段时间都是这样做的:打包,上传到 tower,在 tower 上编写本次更新说明,通知测试。一般情况下,打包及上传的过程大概也就 2 分钟。除此之外,由于项目代码有作混淆,并且使用了 bugly,因此在发出每个版本之后还需要将混淆的 mapping.txt 传到 bugly 上。当日复一日,并且有时还遇到网络较差的情况时,这种人工手动的工作方式就很影响工作效率及心情了。因此,自动化构建及发布就成了必须掌握的技能了。
本篇分享的是我在 Android 自动化构建的一些经验,涉及到的工具及网站如下:
- Gradle
- fir.im
- Gitlab
- gitlab-ci-multi-runner
所述内容包含:
- 使用 Gradle 自动构建并发布到 fir
- 使用 Gitlab-CI,在提交时自动化构建并发布到 fir
- 在服务器配置 Docker 版的 gitlab-ci-multi-runner
- 多 flavor 时,在 fir 上同时发布的解决方案
Gradle 及 fir 带来的解放生产力
构建并上传 apk 到 fir
我接触 fir.im 的时间比较早,那时官方就已经提供了一个命令行打包并上传的工具 fir-cli。但是有两个问题是我难以忍受的:
1. 它需要安装,并且由于使用 ruby 编写,所以还需要 ruby 环境
2. 它会构建所有 flavor 的版本,虽然最后只上传一个(该问题后来已经解决)
于是,在发现它有提供 API 之后,我查阅了下 Gradle 的文档,自己写了一个简单的 fir 发布插件——fir-publish。
这个插件很小很轻,没有使用额外依赖库,网络请求使用的也是 Gradle 本身就有的 http-client 的 API。使用方式如下:
首先在根项目的 build.gradle 中加入以下依赖:
- buildscript {
- repositories {
- jcenter()
- }
- dependencies {
- classpath 'com.githang:fir:0.1.6'
- }
- }
然后在 app 里的 build.gradle 文件末尾加入以下配置:
- apply plugin: 'fir'
- fir {
- apiToken //fir.im上的API token
- bundleId android.defaultConfig.applicationId
- flavor "Test" (如果没有productFlavor,可不配置此项),仅在上传apk时需要
- appName 你的应用名称,仅在上传apk时需要
- icon 应用图标路径,仅在上传图标时需要
- changeLog "更新日志" // 或者使用 file("日志文件路径")
- }
其中的 API token 在你登录 fir 之后,点击自己的账号就可以获取。
该插件向你的 project 添加以下 4 个任务:
- firCert 获取上传凭证
- firIcon 上传图标,依赖于 firCert
- firApk 上传 APK,依赖于 firCert 及 assembleRelease(或 assembleFlavorRelease)
- firAll 上传图标及 APK,依赖于 firIcon 及 firApk
在上面的配置中,更新日志可以直接写在上面,也可以单独创建一个更新日志的文件(推荐),每次要发布时只需要修改这个文件上的更新日志,然后执行以下命令即可自动构建并发布到 fir:
- . / gradlew firAll
注:windows 用户在前面不需要加./
这样,我们只需要让测试人员关注 fir 上的更新动态即可,而不必自己去等待构建完成再上传然后等待上传完成再去写更新日志。
构建时上传 mapping.txt 到 bugly
关于自动上传 mapping.txt 到 bugly 的问题,其实 bugly 本身已有提供相关的 Gradle 插件 bugly。但是它会在每次构建 Release 版本中都执行上传 mapping.txt,而通常我们只是在最终打包版本给测试的时候才需要,所以我修改了一下配置:
- def isPublish = hasProperty("publish")
- bugly {
- appId = '你的appId'
- appKey = '你的appKey'
- execute = isPublish
- upload = isPublish
- }
在这里新增加了一个变量 ,仅在有 publish 属性时才执行上传的命令,这个属性在执行的时候带入,因此我们打包并发布的命令将进一步演变为如下:
- . / gradlew firApk - Ppublish
Gitlab-CI 带来的进一步解放
在上面的过程中,其实我们解决的最大问题是把构建——发布——编写版本更新日志这三个步骤合成一步,少去了中间过程的等待,但是结果还是我们要在每次需要时去手动执行这一步。
CI 类的服务能够让我们把代码推送到服务器上时即可开始构建,使得我们的整个构建过程达到真正的自动化,而不用人工参与。
由于公司使用的是 Gitlab,所以这里只谈 Gitlab-CI 相关的内容。
新版的 Gitlab CI 中使用的是 gitlab-ci-multi-runner,关于它的安装可以到参考其官方文档,这里不再赘述。
需要注意一点的是,在安装之后进行注册时,如果你是想注册为共享 runner(所有项目都可使用),那么第一个问题的地址应该是你们公司 gitlab 的地址,第二个问题的 token 在管理员界面的 runner 配置中可以看到。如果是想注册为私有的 runner,则其 url 与 token 在项目的设置中可以看到。
接下来,只需要在我们的项目的根目录中添加一个. gitlab-ci.yml 文件,并在其中进行 CI 配置,然后提交并推送到我们的 gitlab 上即可。
还是以我这里的公司项目为例。项目采用 Git-flow 流程进行开发,在要发布时会创建 release 分支,因此需要发布到 fir 上给测试人员的是 release 分支及 tag 上的代码,其他分支的代码我们只需要进行构建测试就可以了。在我们公司的项目中,有开发环境 、测试环境及生产环境,分别对应三个 productFlavor:Develop, Test,Official,它们之间只有 API 的地址不同。因此,构建测试使用其中一个环境的就可以了。
所以脚本如下:
- before_script:
- - chmod +x ./gradlew
- compileTest:
- script: "./gradlew clean aDevelopDebug"
- except:
- - /^release.*$/
- - tags
- publishToFir:
- script: "./gradlew clean firApk -Ppublish"
- type: deploy
- only:
- - /^release.*$/
- - tags
在这里,我定义了两个 ci 任务,分别是 compileTest 以及 publishToFir。script 表示该任务所执行的命令。except 表示不对哪些分支进行构建。使用 git-flow 流程时,将发布的分支都是以 release/xxx 来命名,所以这里用正则来表示。only 表示仅对哪些分支执行这个构建任务。type 表示任务的类型。
将配置提交,然后推送到 Gitlab 上,就能够触发 CI 去执行我们所定义的构建任务了。如果你成功了配置了 Gitlab 上的邮箱发送服务,那么我们就可以不用主动去关心这个结果,因为如果构建失败了,Gitlab 将会向我们发送邮件通知。
如果你不想使用 docker 来运行 runner,可跳过下面这一节。
如果你也不需要同时在 fir 上发布不同 flavor 的 APK,那么后面的也不用看了。
更高级的 Docker 版的 CI Runner
上面虽然使用了 gitlab-ci-multi-runner 来完成自动化,但是它是在我本机上跑的。每次编译时占用的内存及 CPU 会对开发略有影响,并且还需要我在每次开机后开个终端运行一下这个 runner。公司内部是有一台 Ubuntu 服务器专门用于代码及项目相目的服务的,如果把我们的 runner 部署到这台服务器上那就更好了。
公司的这台服务器安装了 Docker,其他的服务都是以 docker 形式运行的。既然这样,我也遵守规则用 docker 部署上 runner 吧。
向公司的技术大伽问来内部服务器的管理员账号,又翻了一遍《Docker — 从入门到实践》的 PDF,然后就开始写 Dockerfile 并在自己电脑上试验了。
在踩了 ubuntu 版本安装不了 JDK8、挂载 Android SDK 目录、没有 32 位动态库导致 Android SDK 执行不了,以及中文乱码等坑之后,目前我的 Dockerfile 如下:
- FROM ubuntu:15.04
- MAINTAINER HuangHaohang <msdx.android@qq.com>
- ENV ANDROID_HOME /android-sdk
- RUN apt update && apt install -y openjdk-8-jdk curl
- #如果遇到android-sdk里的命令无法执行,则需要安装32位的动态链接库。
- RUN apt install -y libc6-i386 lib32stdc++6 lib32gcc1 lib32ncurses5 lib32z1
- RUN curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.deb.sh | bash
- RUN apt-get install -y gitlab-ci-multi-runner
- # Ensure UTF-8 locale
- #COPY locale /etc/default/locale
- RUN locale-gen zh_CN.UTF-8 && \
- DEBIAN_FRONTEND=noninteractive dpkg-reconfigure locales
- RUN locale-gen zh_CN.UTF-8
- ENV LANG zh_CN.UTF-8
- ENV LANGUAGE zh_CN:zh
- ENV LC_ALL zh_CN.UTF-8
注:后续如果有变化,将在我的 github 项目上更新,地址为:https://github.com/msdx/dockerfile/blob/master/gitlab-ci-multi-runner/Dockerfile
然后执行 docker build 创建 docker 镜像之后,运行 docker 时挂载上 android sdk 以及可读写的 gradle 缓存目录就可以了,其他问题包括 runner 的注册等可参见我这里的项目说明:https://github.com/msdx/dockerfile/tree/master/gitlab-ci-multi-runner 。由于篇幅原因,这里不作赘述。
我这里把 android-sdk 打包并通过 ssh 上传到了服务器,然后解压在了公司的用户目录的 android-sdk 下,并在该目录创建了一个文件夹 GradleUserHome,用于放 Gradle 的缓存,最终启动这个 docker 的脚本如下:
- #!/bin/bash
- sudo docker run -d \
- --name gitlab-ci-multi-runner \
- --restart always \
- -v /home/irain/android-sdk:/android-sdk:ro \
- -v /home/irain/GradleUserHome:/root/.gradle:rw \
- irain/gitlab-runner:registered \
- gitlab-ci-multi-runner --debug run
fir 上的小技巧——多 flavor 的发布方式
前面提到我们公司的项目 API 地址是有分多个环境的(开发环境、测试环境以及生产环境)。本来我只需要打包测试环境的给测试人员用,生产环境在最终要发布的时候再自己打包。但是在这次新版本的开发中,服务端的人员也希望我能够打包开发环境的 Apk 给他,这样有时候他也可以自测一下。由于项目正在开发中,版本变化较快,所以我也想到通过自动构建发布到 fir 上,再由他自己去下载,这样就可保证他可以获得最新开发的版本。
然而,相当沮丧的一点是,fir 上并不支持同一个应用多环境的发布,虽然这个需求在一年以前就有其他人提出。我问了客服,客服的建议是换一个 bundleId(applicationId),当然这是不可能的,因为我们的应用使用到了高德地图、微信分享及各种支付等许多和 applicationId 关联的 SDK,不可能重新部署一套。最后查看其他人分享的一个实现技巧。
首先,你需要在 fir 上注册一个号(当然你也可以请你同事帮忙),然后把你的应用上传上去,再进入应用的权限控制,把你的大号邀请进来,这样你的大号上就有两个这样的应用了,并且可以对它上传新版本来更新。当然,如果你使用 API 来上传,则不需要邀请,只需要填不同的 API Token 即可。所以最终,我的 app 的 build.gradle 中关于 fir 发布的配置如下:
- def envFlavor = hasProperty("flavor") ? getProperty("flavor") : "Test"
- if (envFlavor == "Develop") {
- fir {
- apiToken "小号的api token"
- bundleId android.defaultConfig.applicationId
- flavor envFlavor
- appName "XXX-开发版"
- changeLog "git show -s --format=%B HEAD".execute().text
- }
- } else {
- fir {
- apiToken "大号的api token"
- bundleId android.defaultConfig.applicationId
- flavor envFlavor
- appName "XXX-测试版"
- changeLog file("./changeLog.txt")
- }
- }
其中,flavor 是通过定义的 envFlavor 来设置,而 envFlavor 根据执行的时候传入的 flavor 属性的值来设置。对应的. gitlab-ci.yml 也修改如下:
- before_script:
- - chmod +x ./gradlew
- compileTest:
- script: "./gradlew clean firApk -Ppublish -Pflavor=Develop"
- except:
- - /^release.*$/
- - tags
- publishToFir:
- script: "./gradlew clean firApk -Ppublish"
- type: deploy
- only:
- - /^release.*$/
- - tags
来源: http://www.phperz.com/article/17/0826/337547.html