在 window7 本机环境下用 Jenkins 对 Android 集成打包。
本文综合 Android Jenkins+Git+Gradle持续集成-实在太详细 和 利用Jenkins玩转Android自动打包发包 这两篇文章而成。
一、基本配置和构建
1. 下载 jenkins
官网 下载地址
2. 安装相应的插件
系统管理>> 管理插件
Branch API Plugin
build timeout plugin
build-name-setter
Credentials Binding Plugin
description setter plugin
Dynamic Parameter Plug-in
Environment Injector Plugin
fir-plugin(可选)
Git plugin(可选)
GIT server Plugin(可选)
Gradle Plugin
Pipeline: Basic Steps
Pipeline: Build Step
Pipeline: Input Step
Pipeline: Nodes and Processes
Pipeline: Stage Step
Post-Build Script Plug-in
SSH Slaves plugin
Subversion Release Manager plugin(可选)
Timestamper
Workspace Cleanup Plugin
Subversion Plug-in(可选)
因为 jenkins 版本更新,Dynamic Parameter Plug-in 这插件因为不安全,已经没有了,不够可以从下面这个网站去下载 然后在 高级>上传插件 中上传即可
详情参考jenkins参数化构建过程添加参数时没有Dynamic Paramter选项怎么办?
3.配置
. 配置Global Tool Configuration
系统管理>> Global Tool Configurantion
这里包括配置 JDK, GIT以及Gradle,具体看截图
我这里配置了 Gradle2.14 和 Gradle 3.4.1,其实配一个就可以了,看个人需求。
.配置 Android 环境
系统管理 >> 全局属性
这里的 SDK 路径要与 Android Studio 里配置的一致
4.新建构建项目
. 进行 git 项目地址的配置
. 添加 Credentials , 就是添加账号和密码
. 构建
增加构建步骤 >> Invoke Gradle script >> Invoke Gradle
选择相应的 Gradle Version, 在 Tasks 中添加相应的 gradle 脚本命令
然后保存,回到项目主页面,选择立即构建即可
备注: 可能出现的问题 构建的时候可能会因为 android build cache 路径过长,导致失败
AAPT err(Facade for 1574801287): \\?\C:\Windows\System32\config\systemprofile\.android\build-cache\a6567330c6202da5c23cba8fdcd5a6cd9ef3e184\output\res\drawable-mdpi-v4\abc_scrubber_control_to_pressed_mtrl_005.png ERROR: Unable to open PNG file
解决方法:单独建立一个 build cache 目录,用于构建,然后在 gradle.properties 中指定刚刚建立的 build cache 路径即可。
android.enableBuildCache=false
android.buildCacheDir=D:\\Android_cache
二、自定义构建
自定义构建需要 Android 项目和 Jenkins 相应的配置
1. 参数化构建过程
jenkins 项目配置中相关的构建
. 配置项目分支
在源码中也要进行相应的修改
. 配置 product flavor
这里的选项必须要与项目中 build.gradle 中是一致
. 是否为 Jenkins 构建
. 构建时间
. 配置构建版本
. 配置构建自定义标签(可选项)
项目 build.gradle 中的配置
apply plugin: 'com.android.application'
// 获取时间戳
def getDate(){
def date = new Date();
def formattedDate = date.format('yyyy-MM-dd-HH-mm')
return formattedDate
}
// 版本号
def verCode = 1
android {
compileSdkVersion 25
buildToolsVersion "25.0.3"
defaultConfig {
applicationId "com.yxhuang.jenkinsdemo"
minSdkVersion 16
targetSdkVersion 25
versionCode verCode
versionName APP_VERSION
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
// 签名信息
signingConfigs {
release {
//由于本地打包使用的是本机上的KeyStore,而Jenkins打包用的是服务器上的KeyStore
//两个路径不一样
if("true".equals(IS_JENKINS)){
storeFile file(KEY_STORE_FILE_PATH_IN_SERVER)
}else {
storeFile file(KEY_STORE_FILE_PATH_IN_LOCAL)
}
}
debug{
}
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
debug {
}
}
packagingOptions {
exclude 'META-INF/DEPENDENCIES.txt'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/notice.txt'
exclude 'META-INF/license.txt'
exclude 'META-INF/dependencies.txt'
exclude 'META-INF/LGPL2.1'
}
// 渠道
productFlavors {
Commonsoon {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "commonsoon"]
}
Zhushou91 {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "zhushou91"]
}
Market360 {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "market360"]
}
Xiaomi {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "xiaomi"]
}
Wandoujia {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]
}
Marketandroid {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "marketandroid"]
}
Yingyonghui {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "yingyonghui"]
}
Jifengmarket {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "jifengmarket"]
}
Mumayi {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "mumayi"]
}
Anzhimarket {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "anzhimarket"]
}
Yingyongbao {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "yingyongbao"]
}
Huaweimarket {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "huaweimarket"]
}
}
//修改生成的apk名字
applicationVariants.all { variant ->
variant.outputs.each { output ->
// 新名称
def newName
// 时间戳
def timeNow
// 输出路径
def outDirectory;
if ("true".equals(IS_JENKINS)) { // 服务器打包
timeNow = BUILD_TIME
outDirectory = BUILD_PATH_IN_SERVER
newName = 'android-v' + APP_VERSION + '-' + variant.productFlavors[0].name + '-' + timeNow + '-' + variant.buildType.name + '.apk'
} else { // 本机打包
timeNow = getDate()
outDirectory = output.outputFile.getParent()
if (variant.buildType.name.equals('debug')) {
newName = "android-v${APP_VERSION}-debug.apk"
} else {
newName = 'android-v' + APP_VERSION + '-' + variant.productFlavors[0].name + '-' + timeNow + '-' + variant.buildType.name + '.apk'
}
}
output.outputFile = new File(outDirectory + '/' + timeNow , newName)
}
}
}
// 定义一个任务来获取 Release Password
task getReleasePassword << {
def keypassword = ''
def storepassword = ''
def keyalies = ''
if (rootProject.file('private.properties').exists()){
Properties properties = new Properties();
properties.load(rootProject.file('private.properties').newDataInputStream())
keypassword = properties.getProperty('release.password')
storepassword = properties.getProperty('release.storepassword')
keyalies = properties.getProperty('release.keyalies')
}
android.signingConfigs.release.keyPassword = keypassword
android.signingConfigs.release.storePassword = storepassword
android.signingConfigs.release.keyAlias = keyalies
}
// 运行时才添加,相当于 Hook
tasks.whenTaskAdded { theTask ->
if (theTask.name.contains("Release")){
theTask.dependsOn "getReleasePassword"
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12'
}
在 gradle.properties 中的配置如下
# 编译路径
android.enableBuildCache=false
android.buildCacheDir=D:\\Android_cache
## APP version name
APP_VERSION = 1.0.1
# 是否为 Jenkins 编译, 默认是 false
IS_JENKINS = false
# 构建时间
BUILD_TIME = ''
# keyStore 在服务器上的路径
KEY_STORE_FILE_PATH_IN_SERVER = E:\\work\\testkeystore\\jenkinsDemo.jks
# keyStore 在本地路径
KEY_STORE_FILE_PATH_IN_LOCAL = E:\\work\\testkeystore\\jenkinsDemo.jks
# 服务器生构建路径
BUILD_PATH_IN_SERVER = D:\\Workspaces\\Jenkins\\apk
因为我这里是在本机的环境下构建的,所以 keyStore 在服务器和本地路径一样的,如果在服务器上构建的话,进行相应的修改即可。
签名信息是在 prviate.properties 上配置的,正常情况下这个文件不用提交到 git 上的。
release.password=123456
release.storepassword=123456
release.keyalies=yxhuang
关于签名等 gradle 详细内容,可以参考我的上一篇文章Gradle for Android 简要记录
配置完成后就可以看到
Build with Paramters 构建生成的 apk
三、二维码下载
构建完成后,apk 包还要去目录中拿,我们可以用二维码展现出来,这样就可以扫码下载安装 apk 了。生成二维码有两种方法,一种是利用蒲公英的二维码,另一种是自己生成二维码。
这里需要用到 Tomcat, 先把 Tomcat 安装好,然后进行一些配置
1. 配置 Tomcat
. 改端口
由于我们之前已经安装了 Jenkins, 它的端口是 8080, Tomcat 的也是,这里会有冲突,所以要改一下 tomcat 的端口。
在安装目录下 conf/server.xml 文件将
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
改为
<Connector port="8084" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
. 将 jenkins 放入到 tomcat 中
将 jenkins 目录下的 war 文件夹里面的所有东西都复制到 tomcat 目录下 webapps 目录的 jenkins 文件夹中。
如果这样时候在浏览器中进入 http://localhost:8084/jenkins 这时候会发现变成了重新安装 jenkins 了。所以我们还需要经 tomcat 中的 jenkins 指向我们之前已经配置好的 jenkins.
在 tomcat 目录下 conf/context.xml 文件中添加
<Environment name="JENKINS_HOME" value="D:\Program Files (x86)\Jenkins" type="java.lang.String"/>
其中 value 值是之前 jenkins 安装的目录
Context>
...
<Valve className="org.apache.catalina.valves.CometConnectionManagerValve" />
-->
<!-- 将 jenkins 目录添加进来 -->
<Environment name="JENKINS_HOME" value="D:\Program Files (x86)\Jenkins" type="java.lang.String"/>
</Context>
然后就可以看到了
1. 利用蒲公英二维码
利用蒲公英的二维码需要用到 curl 命令,在 windows 操作系统是没有,需要安装。可参考这篇文章windows下使用curl命令
. 在构建步骤中选择 Execute windows batch command
其中 file 是配置生成的 apk, uKey 和 api_key 是蒲公英上自己账户的 uKey 和 api_key
. Set build description
设置 build 的描述,展现二维码,在构建后操作选择
. 系统设置 >> Configure Global Security 中 Markup Formatter 选择 Safe HTML
最后生成就可以看到二维码了
2. 自己生成二维码
自己生成二维码需要用到 python 以及 qrcode 这个库。所以先安装好 python 和 clone 下来 qrcode。
还有 python 安装 PLY
. Jenkins 配置 python 环境
在系统管理 >> 系统设置 >> 全局属性中设置
. 构建的路径改为在 tomcat 的目录下
# 服务器生构建路径
BUILD_PATH_IN_SERVER = D:\\Program Files\\Tomcat 8.0\\webapps\\apk
. Python 脚本设置
路径名称因为有空格,要用双引号括起来。需要经 IP 改自己本机的 IP
. 构建后的设置
<img src='http://192.168.13.1:8084/apk/${BUILD_TYPE}/qrcode.png' width=200px height=200px><br><a href=http://192.168.13.1:8084/apk/${BUILD_TIME}/android-v${APP_VERSION}-${PRODUCT_FLAVOR}-${BUILD_TIME}.apk">http://localhost:8084/apk/${JENKINS_TIME}/android-v${APP_VERSION}-${PRODUCT_FLAVOR}-${JENKINS_TIME}-${ENVIRONMENT}.apk</a>