背景: 在移动互联网时代,大多数的企业内部都有移动客户端,而移动客户端又因为OS的不同产生了两个派系,即
安卓(Android)
和苹果(IOS)
,而对于互联网技术从业者来说,这两者最直接的区别就是开源
和闭源
,因而也导致了在构建移动客户端时,为了整体的稳定性和可靠性的考虑(甚至有成本的考虑),需要进行分别编译,本篇文章记录下如何在Linux环境下构建Android
编译环境.
介绍
通常,开发苹果
系列的软件均需要使用一些专有开发工具,比如xcode,而此工具必须运行在Mac OS X
设备上(当然你也可以尝试各种黑苹果的方式),所以不论是对于个人开发者还是企业构建服务器来说,都需要购买更多的Mac
设备,通常,我知道的企业内部会使用Mac Mini
来作为苹果系列的构建环境。
而作为安卓(Android)
系列的软件,由于本身是谷歌开源的移动端操作系统,因此对于底层开发环境和构建环境没有太高的要求。一般而言,开发者会使用Android Studio来开发安卓系列的软件,而内置的命令行工具command-line则默认提供了安卓软件的编译工具和环境。值得一提的是,由于Android Studio
是开源的,因此该工具也提供了多个平台的支持(Windows,Mac,Linux,Chrome OS)。而这也极大的降低了企业的整体成本,通常开发环境可以在任意的OS环境中进行开发,而企业内部也可以使用Linux
环境进行安卓软件的持续集成和交付.
接下来主要讲解下如何在Linux环境下构建安卓的编译环境。
Linux下安卓(Android)编译环境的搭建
前提条件
需要注意的是,对于安卓(Android)
应用来说,一些依赖包的管理主要依靠sdkmanager
这个命令行工具,该工具可以在Android Studio页面找到,并且支持Windows
,Mac
,Linux
三个不同平台的版本。
同时,安卓(Android)
应用的开发工具Android Studio
使用[Gradle](https://github.com/gradle/gradle)
来进行编译和打包,因此对于安卓(Android)
应用而言,也将使用gradle
来进行编译和打包操作,该软件可以在Gradle页面找到。
其次,[NDK(原生开发套件)](https://developer.android.google.cn/ndk/guides/)
是一套工具,可以使开发者能够在 Android 应用中使用 C 和 C++ 代码,并提供众多平台库,开发者可使用这些平台库管理原生 Activity 和访问物理设备组件,例如传感器和轻触输入。该开发套件可以在NDK页面找到.
最后,如上几个组件的底层语言均使用java
进行开发,因此需要安装JDK
相关环境。
综上所述,在Linux环境下编译安卓(Android)环境需要如下几个组件:
- JDK: java语言的基础编译和运行环境
- sdkmanager: 安卓(Android)应用下的依赖包管理器
- NDK: 安卓原生开发套件,可调用底层
C
和C++
代码 - Gradle: 安卓系列软件编译工具(类似
maven
之类的工具)
快速安装基本环境
# 下载基础软件包
$ mkdir -p /opt/servers/ && cd /opt/servers/
# JDK(可以选择openjdk)
$ wget http://dl.bgbiao.top/dav/jdk1.8.0_191.tar.gz
$ tar -zxf jdk1.8.0_191.tar.gz -C /opt/servers/
# 下载并配置sdkmanager
$ wget https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip
$ unzip sdk-tools-linux-4333796.zip
$ mkdir -p /opt/sdk
$ ln -s /opt/servers/tools /opt/sdk/tools
# 配置环境变量
$ cat /etc/profile
export JAVA_HOME=/opt/servers/jdk1.8.0_191
export CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$CLASSPATH
export PATH=${JAVA_HOME}/bin:${PATH}
export ANDROID_HOME=/opt/sdk
export PATH=${ANDROID_HOME}/tools/bin:${ANDROID_HOME}/tools/bin:${ANDROID_HOME}:${PATH}
# 测试sdkmanager使用
$ source /etc/profile
$ sdkmanager --list
Warning: File /root/.android/repositories.cfg could not be loaded.
Installed packages:
Path | Version | Description | Location
------- | ------- | ------- | -------
tools | 26.0.1 | Android SDK Tools 26.0.1 | tools/
# 安装指定版本的包
$ sdkmanager --list | grep cmake
Warning: File /root/.android/repositories.cfg could not be loaded.
cmake;3.10.2.4988404 | 3.10.2 | CMake 3.10.2.4988404
cmake;3.6.4111459 | 3.6.4111459 | CMake 3.6.4111459
$ sdkmanager 'cmake;3.6.4111459'
....
# 下载并安装ndk
$ cd /opt/servers && wget https://dl.google.com/android/repository/android-ndk-r16b-linux-x86_64.zip
$ unzip android-ndk-r16b-linux-x86_64.zip
$ ln -s /opt/servers/android-ndk-r16b /opt/ndk
# 配置ndk配置环境(增加如下配置)
$ cat /etc/profile
export NDK_HOME=/opt/ndk
export ANDROID_NDK_HOME=/opt/ndk
export PATH=$NDK_HOME:${ANDROID_NDK_HOME}:$PATH
# 下载并安装gradle
$ cd /opt/servers && wget https://services.gradle.org/distributions/gradle-4.10.1-all.zip
$ unzip gradle-4.10.1-all.zip
$ ln -s /opt/servers/gradle-4.10.1 /opt/gradle
# 配置gradle环境(增加如下配置)
$ cat /etc/profile
export GRADLE_HOME=/opt/gradle
export PATH=${GRADLE_HOME}/bin:${PATH}
# 测试gradle
$ gradle -v
------------------------------------------------------------
Gradle 4.10.1
------------------------------------------------------------
Build time: 2018-09-12 11:33:27 UTC
Revision: 76c9179ea9bddc32810f9125ad97c3315c544919
Kotlin DSL: 1.0-rc-6
Kotlin: 1.2.61
Groovy: 2.4.15
Ant: Apache Ant(TM) version 1.9.11 compiled on March 23 2018
JVM: 1.8.0_191 (Oracle Corporation 25.191-b12)
OS: Linux 3.10.0-862.el7.x86_64 amd64
安装Android基础依赖
注意:
正式编译之前先生成license,并将licenses
目录移动到/opt/sdk/
下,和sdkmanager
的tools目录平级
# 安装android基础依赖包
$ sdkmanager 'build-tools;28.0.3' 'platforms;android-28' 'cmake;3.6.4111459'
# 生成licences
$ sdkmanager --licenses
$ cp -rp licenses /opt/sdk/
编译Android包
# 加载下整体环境变量
$ cat /etc/profile
....
....
export JAVA_HOME=/opt/servers/jdk1.8.0_191
export CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$CLASSPATH
export PATH=${JAVA_HOME}/bin:${PATH}
export ANDROID_HOME=/opt/sdk
export PATH=${ANDROID_HOME}/tools/bin:${ANDROID_HOME}/tools/bin:${ANDROID_HOME}:${PATH}
export GRADLE_HOME=/opt/gradle
export PATH=${GRADLE_HOME}/bin:${PATH}
export NDK_HOME=/opt/ndk
export ANDROID_NDK_HOME=/opt/ndk
export PATH=$NDK_HOME:${ANDROID_NDK_HOME}:$PATH
$ source /etc/profile
# 下载安卓(android)应用的源码文件
$ git clone http://git.bgbiao.top/test-app.git
$ cd test-app
$ export ENV="fNormal"
$ gradle clean assemble${ENV}Release
Starting a Gradle Daemon, 2 busy Daemons could not be reused, use --status for details
Parallel execution with configuration on demand is an incubating feature.
<-------------> 0% CONFIGURING [29s]
> root project > Resolve dependencies of :classpath > guava-18.0.pom > 3 KB/5 KB downloaded
> IDLE
> root project > Resolve dependencies of :classpath > commons-lang-2.6.pom > 9 KB/17 KB downloaded
> root project > Resolve dependencies of :classpath > osdetector-gradle-plugin-1.4.0.pom
....
....
BUILD SUCCESSFUL in 7m 7s
501 actionable tasks: 164 executed, 288 from cache, 49 up-to-date
# 查看生成的apk包
$ ls app/build/outputs/apk/fNormal/release/
app-fNormal-release.apk output.json
# 安装二维码生成器
$ yum install qrencode-3.4.1-3.el7.x86_64 -y
# 将生成的apk包上传到指定的http服务中
$ curl -T app/build/outputs/apk/fNormal/release/app-fNormal-release.apk http://dl.bgbiao.top/dav/
# 给apk下载文件生成一个二维码
$ qrencode -o test-android.png "http://dl.bgbiao.top/dav/app-fNormal-release.apk"
# 上传二维码
$ curl -T test-android.png http://dl.bgbiao.top/dav/
接下来,开发者用户即可以使用http://dl.bgbiao.top/dav/test-android.png
二维码地址进行扫描安装,对该版本的app功能进行测试验证了。
需要注意的是,通常情况下,开发者如果使用Mac OSX
来编写代码,可能会在代码里使用类似#include 'MD5.h'
之类的代码,看起来好像没有什么问题,但是因为Mac OSX
或Windows
系统中对大小写不敏感,所以那样写不会有什么太大影响,因为编译器可以找到系统中的md5.h
,但是在Linux
环境下,系统对大小写很敏感,如果代码里写死了MD5.h
,而系统库中是md5.h
,那肯定会编译失败,而且一般人看到该异常情况不会想到是大小写的问题。
好了,趟坑算是趟完了,接下来提供一个福利,我自己基于以上构建历史环境打包了一个docker镜像,用以封装Android
编译的基本环境.
docker镜像
$ cat Dockerfile
FROM centos:7.5.1804
MAINTAINER "BGBiao <https://bgbiao.top/>"
ENV TZ "Asia/Shanghai"
RUN yum clean all && \
yum install unzip wget curl -y && \
mkdir -p /opt/{servers,app} && \
cd /opt/servers/ && \
wget http://dl.bgbiao.top/hadoop/jdk1.8.0_191.tar.gz && \
wget http://dl.bgbiao.top/dav/android-build/android-ndk-r16b-linux-x86_64.zip && \
wget http://dl.bgbiao.top/dav/android-build/gradle-4.10.1-all.zip && \
wget http://dl.bgbiao.top/dav/android-build/sdk-tools-linux-4333796.zip
RUN pushd /opt/servers && \
tar -zxf jdk1.8.0_191.tar.gz && \
unzip android-ndk-r16b-linux-x86_64.zip && \
unzip gradle-4.10.1-all.zip && \
unzip sdk-tools-linux-4333796.zip && \
mkdir -p /opt/sdkmanager && \
ln -s /opt/servers/tools /opt/sdkmanager/tools && \
ln -s /opt/servers/gradle-4.10.1 /opt/gradle && \
ln -s /opt/servers/android-ndk-r16b /opt/ndk
COPY profile /opt/servers/setenv.sh
$ cat profile
export JAVA_HOME=/opt/servers/jdk1.8.0_191
export CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$CLASSPATH
export PATH=${JAVA_HOME}/bin:${PATH}
export ANDROID_HOME=/opt/sdkmanager
export PATH=${ANDROID_HOME}/tools/bin:${ANDROID_HOME}/tools/bin:${ANDROID_HOME}:${PATH}
export GRADLE_HOME=/opt/gradle
export PATH=${GRADLE_HOME}/bin:${PATH}
export NDK_HOME=/opt/ndk
export ANDROID_NDK_HOME=/opt/ndk
export PATH=$NDK_HOME:${ANDROID_NDK_HOME}:$PATH
# 用户可以根据上述Dockerfile构建镜像,同时也可以直接使用我构建好的一个镜像
$ docker pull xxbandy123/android-build-env:19-12-12
# 使用方式
# 基于上述镜像,用户需要使用sdkmanager 安装依赖的安卓库,同时编排好自己的gradle打包命令,在自己的安卓项目中直接编译即可
$ docker run -itd --name android-build-env:19-12-12 bash
[root@4c05d4ded28d /]# source /opt/servers/setenv.sh
[root@4c05d4ded28d /]# git clone your-android-app.git
[root@4c05d4ded28d /]# sdkmanager 'build-tools;28.0.3' 'platforms;android-28' 'cmake;3.6.4111459'
[root@4c05d4ded28d /]# sdkmanager --licenses
[root@4c05d4ded28d /]# cp -rp licenses ${ANDROID_HOME}/
[root@4c05d4ded28d /]# ls ${ANDROID_HOME}
build-tools cmake licenses platforms platform-tools tools
# 开始执行编译(之后就是漫长的等待了)
[root@4c05d4ded28d /]# cd your-android-app && gradle clean assembleRelease
注意事项
- 1.通常持续集成会使用
Jenkins
来进行编译打包,因此以上环境再接入Jenkins-salve之前需要安装git
客户端名 - 2.通常客户端在测试包时会通过二维码扫描来下载包,因此环境上需要安装
qrencode
软件,用于生成二维码 - 3.需要注意
gradle
在编译过程中会启动一个守护进程,如果编译异常结束,该守护进程可能不会立即释放,此时立即再次编译将有可能导致OOM
欢迎关注我的公众号: BGBiao,一起进步~