背景
Jenkins上目前需要增加对安卓流水线的支持,研究了下,发现核心流程就是通过gradle命令,来调用android sdk生成apk包,再把apk作为制品放到Jenkins上即可。
环境准备
主要就三个,jdk、gradle和android sdk。
jdk
gradle要依赖jdk才能运行,如果图省事,直接让gralde调用Jenkins宿主机上jdk就行,但我Jenkins宿主机的JDK是OpenJDK 1.8的,因为不知道哪天gradle会抛弃jdk 1.8,所以我用adoptopenjdk:8-jdk-hotspot 作为Docker基础镜像来调用gradle,这样哪天要升级jdk,换一个基础镜像即可。
gradle
gradle的作用类似于maven,搞安卓开发的同学肯定或多或少会了解过它,每次要生成apk时,就在AS的界面上点一下clean、assemble、assembleDebug、assembleRelease的按钮。
其实AS在你点击之后,也是调用的你电脑上的gradle来对项目进行清理和打包。具个调用哪个gradle呢?如果你是AS新建的安卓工程,那么在你的工程目录下肯定有几个名称带有gradle的文件:
build.gradle中指定了你所需要的gradle插件版本(图中3.6.3),gradlew和gradlew.bat分别是linux和windows下的gradle可执行脚本,你点击AS界面上的asseble按钮时,其实就是执行的就是这个脚本里的内容。这个脚本会去调用gradle/wrapper目录下的文件,来下载与gradle插件版本对应的gradle工具,工具的版本是在gradle-wrapper.properties中指定的,但这个版本不是想写啥就是啥,它与gradle插件版本有个对应关系,见gradle插件与gradle工具版本对应关系
这里展开讲一下gradle-wrapper.properties这个文件,如图
当你使用gradle打包过几次apk后,可以找一下你的gradle的目录下,会有个wrapper/dists目录,里面有跟你在gradle-wrapper.properties里面指定的版本一样的gradle目录
关于gradle插件和gradle工具之间的版本,也可以在AS的界面上进行调整
看到这里,大家应该也就明白了,gradle其实是不需要像maven那样单独安装的,因为AS已经把gradle事合到了android的工具目录里,只要你提交的代码中把图中的几个目录一起上传到git就行。
其实这是gradle的一个功能特性,为了避免不同项目依赖的gradle版本有冲突,gradle对自己进行了一层封装gradlew。gradlew具备gradle一样的功能,AS实际上是调用的项目下的gradlew。
android sdk
android sdk才是重头戏。在AS里安装android sdk很容易,直接在界面上勾上你所需要的安卓版本的sdk platform就行,但到了CentOS上,没有了AS的IDE环境,咋安装android sdk法?
其实在安卓开发者官网上,是可以单独下载android sdk的,称之为Command line tools。
这个文件是用来管理android sdk的,可以通过命令行来下载、更新、删除sdk platform
2023-12-18更新:官方下的最新版需要使用JDK17才能正常运行,如需要旧版本,可去一文了解:Android cmdline-tools 版本与其最小JDK关系 下载历史版本,下图来源上述文档,侵删
将Linux版本的这个zip文件下载、上传、解压到CentOS的目录中,我这里是放到了/home/android/sdk/cmdline-tools/latest目录下:
这个工具要求必须是放在以 /cmdline-tools/latest 命名的目录下才行,不然在执行的过程中会弹错要求你指定sdk_root。换言之,如果你是放在 /cmdline-tools/latest目录下,那么cmdline-tools的上一级目录就是sdk_root。
进入bin目录,执行./sdkmanager --list,可以列出来所有可以安装的android platform及其它工具
我们需要的一般只是build-tool和platforms,这里安装最新版的安卓sdk,执行 ./sdkmanager “build-tools;32.0.0” “platforms;android-32” ,如果要安装其它版本依次往后加即可,注意用 " 括起来。安装完成后还需要执行 ./sdkmanager --licenses 授权sdk的license,一路y下去即可,如果不这样后面编译apk时会提示license没授权。后续如果要安装其它版本的sdk,可以回到这里添加其它版本即可,注意每添加一个版本都需要重新授权,另外如果要更新/删除某个版本,执行./sdkmanager --update或者–uninstall。
安装完之后,回到sdk目录(我这里是/home/android/sdk),可以发现多了几个文件夹,各个文件夹中的作用见Android SDK Tools,Platform-tools,Build-tools分别有什么作用?,我们主要是会用到tools文件夹下的工具。
到这一步基本上就完成了android sdk的安装,可以开始使用Jenkins来进行安卓的编译了。
这里先回到Android Studio工程下的一个文件local.properties,它指定了你的系统sdk的安装目录,在我windows下是D:\android\sdk,但是刚才在CentOS下这里需要改成/home/android/sdk。可是我们肯定不能指望整个公司的程序员每次提交代码的时候去手动修改这里,所以需要在服务器上去调整。
Jenkins流水线
拉代码
这里就不展开说了,用的Jenkins的gitlab插件去拉
checkout([$class: 'GitSCM', branches: [[name: "{{BUILD_GITSCM_DATA_BRANCH}}"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "{{BUILD_GITSCM_DATA_CREDENTIALID}}", url: "{{BUILD_GITSCM_DATA_GITURL}}"]]])
打包
所谓打包,其实就是执行gradle clean assemb命令。但上文我提过我不准备直接调用Jenkins自带的jdk,而是转用Docker,所以我做了一个带jdk的Docker image来执行gradlew,dockerfile如下:
FROM adoptopenjdk:8-jdk-hotspot
RUN echo "Asia/Shanghai" > /etc/timezone
RUN set -o errexit -o nounset \
&& echo "Adding gradle user and group" \
&& groupadd --system --gid 1000 gradle \
&& useradd --system --gid gradle --uid 1000 --shell /bin/bash --create-home gradle \
&& mkdir /home/gradle/.gradle \
&& chown --recursive gradle:gradle /home/gradle \
&& echo "Symlinking root Gradle cache to gradle Gradle cache" \
&& ln -s /home/gradle/.gradle /root/.gradle \
&& echo "install dos2unix and vim" \
&& apt-get update \
&& apt-get install --yes --no-install-recommends \
dos2unix \
vim \
&& rm -rf /var/lib/apt/lists/*
# 这个docker需要外挂宿主机三个目录
# 这个里面是android sdk生成的一些文件,例如debug.keystore等,必须挂载
VOLUME /root/.android
# 这个是安卓sdk的目录,必须挂载
VOLUME /home/android/sdk
# 这个里面是gradlew工具,不挂也行,但是不挂的话就每次都需要重新下载gradlew工具,影响打包速度
VOLUME /home/gradle/.gradle
WORKDIR /home/application
# 以下部分做了两件事,1是使用dos2unix将用户提交的gradlew脚本改成linux能识别的格式(主要是换行符),2是修改local.properties中的sdk路径
RUN echo "#!/bin/bash" > /home/assemb.sh \
&& if [ -f /home/application/local.properties ];then echo "sed -i 's/sdk\.dir=.*/sdk\.dir=\/home\/android\/sdk/g' local.properties" >> /home/assemb.sh ;else echo "echo sdk.dir=/home/android/sdk > /home/application/local.properties">> /home/assemb.sh;fi \
&& echo 'dos2unix ./gradlew && chmod +x ./gradlew && ./gradlew clean assemb' >> /home/assemb.sh && chmod +x /home/assemb.sh
CMD ["/bin/bash","/home/assemb.sh"]
在Jenkins里调用这个镜像的方法如下
sh 'docker run --rm -v $(你的安卓工程目录,也就是gradlew所在的目录):/home/application -v /root/.android:/root/.android -v /home/gradle/.gradle/wrapper:/home/gradle/.gradle/wrapper -v /home/android/sdk:/home/android/sdk -w /home/application --privileged=true ' + "${docker_server}/androidadoptjdk:1.8 | tee build_log.log"
结合刚才的dockerfile,实际上执行的就是gradlew clean assemb。此时gradlew会自动去下载所需要的gradle工具,并调用androidsdk对代码进行打包apk。
这一步结束其实apk就已经生成了,而且会同时生成debug和release版本(如果在build.gradle中指定了的话),如果只要debug/release版本,可以在dockerfile中调整一下gradlew clean assembleDebug或者gradlew clean assembleRelease。
获取apk版本
gradle很坑的一点,是不能像maven命令一样去获取安卓项目的应用名和版本号,因此只能通过安卓的apkanalyzer工具来反向分析apk。在Jenkins中的命令如下:
# 应用名
sh 'docker run --rm -v $(你的安卓工程目录,也就是gradlew所在的目录):/home/application -v /root/.android:/root/.android -v /home/gradle/.gradle/wrapper:/home/gradle/.gradle/wrapper -v /home/android/sdk:/home/android/sdk -w /home/application --privileged=true ' + "${docker_server}/androidadoptjdk:1.8 " + 'sh -c "find ./ -name *.apk|head -1|xargs -i /home/android/sdk/tools/bin/apkanalyzer manifest application-id {}|xargs -i echo {} > app_name"'
# 应用版本
sh 'docker run --rm -v $(你的安卓工程目录,也就是gradlew所在的目录):/home/application -v /root/.android:/root/.android -v /home/gradle/.gradle/wrapper:/home/gradle/.gradle/wrapper -v /home/android/sdk:/home/android/sdk -w /home/application --privileged=true ' + "${docker_server}/androidadoptjdk:1.8 " + 'sh -c "find ./ -name *.apk|head -1|xargs -i /home/android/sdk/tools/bin/apkanalyzer manifest version-code {}|xargs -i echo {} > app_version"'
收集制品
Jenkins自带的archive命令,收集所有目录下的所有apk结尾的文件即可。
archiveArtifacts '**/*.apk'
效果
流程:
制品: