接上篇 《模块化后的Android App自动构建(一)》。
这一篇讲两方面,app的构建
以及分模块后遇见的问题和解决
。
目前,这两篇提到的脚本都在常态化使用中。如果有同学参考的过程中遇见什么问题,欢迎交流。
二、App的构建
准备条件见上一篇《模块化后的Android App自动构建(一)》 。
1、主模块的build.gradle配置
sdk 版本类型都统一使用gradle.properties配置的,与子模块保持统一
android {
compileSdkVersion Integer.parseInt(System.properties['compileSdkVersion'])
buildToolsVersion System.properties['buildToolsVersion']
defaultConfig {
minSdkVersion Integer.parseInt(System.properties['minSdkVersion'])
targetSdkVersion Integer.parseInt(System.properties['targetSdkVersion'])
ndk{
abiFilters "x86"
abiFilters "armeabi"
}
}
}
2、Jenkins的配置
在Jenkins上正常new item,基本信息配置没有什么特别,按照自己需要填写即可,三个注意点同上篇文章提到的,就是构建脚本不一样和构建结果处理不一样。
-
构建脚本跟着三个参数,git地址写死在脚本里。
-
参数一:
${JOB_NAME}
项目名称 -
参数二:分支名
"develop"
-
参数三:build type
"forTest"
python ${JENKINS_HOME}/workspace/publish/jenkins/shop/build.py ${JOB_NAME} "develop" "forTest"
-
构建结果需要提取apk和mapping文件,这里.html不是必须的,只是因为我自己加了findbugs的执行,这是执行结果。
outputs/apk/*.apk, outputs/mapping/**/mapping.txt,outputs/*.html
3、构建脚本
这里的脚本,区分了测试包和release发布包,因为发布包需要打多个渠道包,下面主要贴出release包的脚本。主要思路同library的打包:
-
新建路径sourcecode,把project 根目录下的相关配置复制过来
-
再pull 主模块的代码,然后
gradle 构建
。 -
然后通过将以渠道名命名的空文件写入
META-INF
来打多个渠道包。 -
最后把构建完成的结果文件(
*.apk,mapping.txt
)copy到需要的路径
#coding:utf-8
#针对不同APP 进行配置
GIT_REPO = "git@git.showjoy.net:shopandroid/shopandroid.git"
# 以下配置无需修改
OUTPUTS_PATH = "/var/lib/jenkins/workspace/"
GRADL_HOME = "/root/gradle-2.10/bin/"
SOURCE_CODE = "sourcecode"
BUILD_TYPE = "release"
DECODE_PATH = "decode"
OUTPUTS_PATH_TMP = "outputs"
CONFIG_PATH = "../../shop_gradle_config"
#把某一目录下的所有文件复制到指定目录中
def copyFiles(sourceDir, targetDir):
for file in os.listdir(sourceDir):
sourceFile = os.path.join(sourceDir, file)
targetFile = os.path.join(targetDir, file)
if os.path.isfile(sourceFile):
if not os.path.exists(targetDir):
os.makedirs(targetDir)
if not os.path.exists(targetFile) or(os.path.exists(targetFile) and (os.path.getsize(targetFile) != os.path.getsize(sourceFile))):
open(targetFile, "wb").write(open(sourceFile, "rb").read())
if os.path.isdir(sourceFile):
First_Directory = False
copyFiles(sourceFile, targetFile)
def chdir(dir):
os.chdir(dir)
print ('当前目录:' + os.getcwd())
def getVersionName(path):
import re
versionName = ""
f = open(path)
for line in f:
searchObj = re.search( r'versionName "(.*)"', line, re.M|re.I)
if searchObj:
versionName = searchObj.group(1)
break
return versionName
def timeCost(startTime):
cost_time = (time.time() - startTime)
minutes = cost_time/60
seconds = cost_time - 60 * minutes
print ('当前用时 : %d mins %d secs' %(minutes, seconds))
if __name__ == "__main__":
import shutil
import sys
import os
content = "n"
print sys.path[0]
chdir(sys.path[0])
import time
start_time = time.time()
print ('start time %f' %start_time)
# 开始读取参数
if len(sys.argv) < 3:
print "构建脚本 参数不够2个,第一个是脚本路径,第二个是项目名称,第三是构建分支"
exit()
if len(sys.argv) >= 4:
BUILD_TYPE = sys.argv[3]
pass
OUTPUTS_PATH = OUTPUTS_PATH + sys.argv[1] + "/outputs"
if os.path.exists(OUTPUTS_PATH):
shutil.rmtree(OUTPUTS_PATH)
# git 地址
branch = sys.argv[2]
module = GIT_REPO
print ('代码地址:' + module + ",分支:" + branch)
# 获取项目名称
import re
MODULE_NAME = re.search(r'/(.*)\.git', GIT_REPO).group(1)
print "项目名称:" + MODULE_NAME
# clone代码
if os.path.exists(SOURCE_CODE):
shutil.rmtree(SOURCE_CODE)
os.mkdir(SOURCE_CODE)
chdir(SOURCE_CODE)
print ('start to clone codes')
os.system("/usr/bin/git clone -b " + branch + " " + module)
# 读取版本号
versionName = getVersionName(MODULE_NAME + "/publish.gradle")
print ("版本号为:" + versionName)
# 复制gradle配置文件
chdir(os.path.dirname(os.getcwd()))
print ('copy files from gradle_config to ' + SOURCE_CODE)
copyFiles(CONFIG_PATH, SOURCE_CODE)
# 进入sourcecode folder
chdir(SOURCE_CODE)
# 写入setting.gradle
setting_file = open('settings.gradle', "wb")
setting_file.write("include ':" + MODULE_NAME + "'")
setting_file.write('\n')
setting_file.close()
if os.path.exists("outputs"):
shutil.rmtree("outputs")
os.mkdir("outputs")
# 开始编译
print ('start to build apk')
os.system(GRADL_HOME + "gradle clean assemble" + BUILD_TYPE)
# 开始打不同的渠道包
chdir(os.path.dirname(os.getcwd()))
market = "channels.txt"
if os.path.exists(OUTPUTS_PATH_TMP):
shutil.rmtree(OUTPUTS_PATH_TMP)
os.mkdir(OUTPUTS_PATH_TMP)
OUTPUTS_PATH_TMP = OUTPUTS_PATH_TMP + "/" + versionName
copyFiles(SOURCE_CODE + "/" + MODULE_NAME + "/build/outputs", OUTPUTS_PATH_TMP)
apk_file = OUTPUTS_PATH_TMP + "/apk/" + MODULE_NAME + "-release.apk"
from shutil import copyfile
print ("read channel from " + market)
import zipfile
f = open(market)
for line in f:
channel = line
channel = channel.strip()
if len(channel) == 0:
pass
print ('channel:' + channel + ", version: " + versionName)
new_apk_aligned_name = OUTPUTS_PATH_TMP + "/" + MODULE_NAME + "-" + channel + "-release-" + versionName + ".apk"
copyfile(apk_file, new_apk_aligned_name)
zipped = zipfile.ZipFile(new_apk_aligned_name, 'a', zipfile.ZIP_DEFLATED)
empty_channel_file = "META-INF/channel_{channel}".format(channel=channel)
zipped.write(SOURCE_CODE + "/channel_empty_file.txt", empty_channel_file)
zipped.close()
timeCost(start_time)
copyFiles(OUTPUTS_PATH_TMP, OUTPUTS_PATH)
shutil.rmtree(SOURCE_CODE)
end_time = time.time()
print ('end time %f' %end_time)
timeCost(start_time)
print ('打包完成,发布之前请先完成自测')
三、分模块后构建遇到的问题和解决
1、子模块在多个app共用,统一跳转协议scheme的设置
拆成多个module后,相应的activity也会写到子module的AndroidManifest.xml
里,里面需要配置跳转协议,如:
这里 scheme
会写死成 “showjoyshop”
。如果这个模块只被一个应用依赖,那并没有什么问题,而如果这个模块被多个应用依赖,那 scheme
就需要改成对应应用的 scheme
。所以就做了如下了优化:
这个 scheme
的值会通过gradle来配置:
android {
......
defaultConfig {
manifestPlaceholders = [SCHEME: System.properties['scheme']]
}
}
可是这个配置不能写在子模块的build.gradle里,如果写在子模块的build.gradle那构建出来的aar还是会被替换成当前project的scheme
,还是无法被其他App依赖。
所以需要将这个配置放置到主模块的build.gradle
里,只有当app 构建的时候,才去替换合并后Androidmanifest.xml
里的 scheme
。
这样在本地开发的时候,还有可能遇到这样的一个问题,这个问题的出现是在只在setting.gradle
里 include
了module,但是没有在主模块里 compile
,就会出现:
所以,就需要在本地开发环境里gradle.properties里配置参数libraryBaseGradle指定的的gradle文件里加上scheme的设置:
android {
......
defaultConfig {
manifestPlaceholders = [SCHEME: System.properties['scheme']]
}
}
而在构建主机上的gradle.properties里配置参数libraryBaseGradle指定的的gradle文件去掉以上配置。
如此便完美解决了该问题。
赏一个 您的支持是对我最大的鼓励。加班再忙,也要熬夜继续码字分享! 码字辛苦 赏个五毛 支持: 微信支付 支付宝好人一生平安 谢谢!
微信支付 支付宝用 [微信] 扫描二维码打赏
用 [支付宝] 扫描二维码打赏