一、前言:
Android打包是一件比较繁琐又耗时的操作,相信很多朋友都想自己搞一个属于自己的自动化构建程序,本文则是对这一程序的实现提供一个思路方法,没有采用 Jenkins持续集成 这种方式来实现,但大概思路都大同小异。
关于本文需要了解的一些东西:
1. 实现思路为:使用JavaGUI做一个zip打包工具,将自定义配置信息写入项目并打包成zip格式,最终将文件拖拽上传至服务器,服务器进行一系列的操作:解压,读取配置,打包,加固,多渠道,最后下发至浏览器端进行自动下载,由于公司电脑是WindowsServer2012,所以内部实现是基于Windows命令的
2.Android项目构建配置都各有不同,所以本文主要提供一个实现思路,感兴趣的朋友可以自己动手实现一个更适合自己的构建工具
3.项目分为GUI端和Web端,均为Java实现,大部分操作都是流/IO,字符串操作,简单易懂
4.使用GUI端需要的配置:JDK环境变量
5.使用Web端需要的配置:IntelliJ IDEA、Tomcat服务器、JDK环境变量、Gradle环境变量、360加固宝PC端、AndroidSDK、Android端所使用到的所有签名文件
6.开发工具,环境变量配置等本文就不多罗嗦了,毕竟不是使用说明书,但是会讲一下Tomcat与IDEA配置所产生的1099端口冲突的坑,还有Gradle多配置
7.项目地址:
GUI端:[AutoBuildForAndroid-GUI](https://github.com/SmartKidsLOL/AutoBuildForAndroid-GUI
Web端:[AutoBuildForAndroid-WEB](https://github.com/SmartKidsLOL/AutoBuildForAndroid-Web
二、GUI端的实现效果图
三、实现思路
如图所示,GUI端很简单,使用也很简单,界面也很丑(请不要在意这些细节…),因为公司的项目是加入了Tinker热修复,考虑到对其他项目的适用性,所以目前只做了2个自定义配置,如图所示,1是选择打包方式是以普通Release或者Tinker热修复包,2是输入项目需要用到的打包签名文件名。点击开始打包后便将这2个配置以Key-Value方式存入Properties文件,以便服务器以流的方式取出来存入。写入配置后使用Zipentry压缩流方式将项目文件夹压缩为Zip格式输出到指定目录。
项目以Mvp简单架构实现的,打包时会简单校验一下是否为Android项目文件夹。
注:自动化打包思路都是以修改并自定义配置Gradle文件,但是前提是公司的业务及项目都可以用同样的Gradle配置,比如三方依赖,ndk配置等主要配置都相同,但只有签名,ApplicationID这些可变配置根据不同项目而不同,这种情况下可以将这些可变的变量配置写入到GUI界面中,对同类型的项目进行不同的变量配置即可,最后直接将这些配置写入到Gradle中,上传到服务器进行一系列操作即可。
所以根据实际情况,我并没有将配置写入Gradle文件,而是另存为Properties文件,这种方式也是想以不动不同项目的gradle配置前提,完成自动化打包。
四、使用方式
在你想要进行自动化打包的项目里加入以下2点:
1.在你的Android项目根目录下创建 keystore.propertie 文件,加入以下内容:
KEY_PATH=D\:/tools/your.jks // 改成你的签名文件地址
KEY_PASS=yourTest
ALIAS_NAME=yourTest
ALIAS_PASS=yourTest
2.打开app下的gradle中加入以下代码:
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(rootProject.file("keystore.properties")))
android {
...
signingConfigs {
release {
try {
storeFile file(keystoreProperties['KEY_PATH'])
storePassword keystoreProperties['KEY_PASS']
keyAlias keystoreProperties['ALIAS_NAME']
keyPassword keystoreProperties['ALIAS_PASS']
} catch (Exception ex) {
throw new InvalidUserDataException(ex.toString())
}
}
}
buildTypes {
release {
...
signingConfig signingConfigs.release
...
}
}
...
}
上述代码和Java中使用流读取Properties文件无异。
Tips:如果你项目使用了Tinker,则可以自己指定基准包生成目录,方便自定义服务器端进行路径配置,附上一份个人的Tinker配置Gradle文件,最后在app下的gradle中进行apply引入,如果有需要,则可以参考
apply plugin: 'tinkerpatch-support'
/**
* TODO: 请按自己的需求修改为适应自己工程的参数
*/
def infoPath = file("${rootDir}/infoBak")
def baseInfo = "$rootProject.ext.baseInfoName"
def variantName = "$rootProject.ext.variantName"
/**
* 对于插件各参数的详细解析请参考
* http://tinkerpatch.com/Docs/SDK
*/
tinkerpatchSupport {
/** 可以在debug的时候关闭 tinkerPatch **/
/** 当disable tinker的时候需要添加multiDexKeepProguard和proguardFiles,
这些配置文件本身由tinkerPatch的插件自动添加,当你disable后需要手动添加
你可以copy本示例中的proguardRules.pro和tinkerMultidexKeep.pro,
需要你手动修改'tinker.sample.android.app'本示例的包名为你自己的包名, com.xxx前缀的包名不用修改
**/
tinkerEnable = true
reflectApplication = true
/**
* 是否开启加固模式,只能在APK将要进行加固时使用,否则会patch失败。
* 如果只在某个渠道使用了加固,可使用多flavors配置
**/
protectedApp = true
/**
* 实验功能
* 补丁是否支持新增 Activity (新增Activity的exported属性必须为false)
**/
supportComponent = true
autoBackupApkPath = "${infoPath}"
appKey = "$rootProject.ext.appKey"
/** 注意: 若发布新的全量包, appVersion一定要更新 **/
appVersion = "$rootProject.ext.versionName"
def pathPrefix = "${infoPath}/${baseInfo}/${variantName}/"
def name = "${project.name}-${variantName}"
baseApkFile = "${pathPrefix}/${name}.apk"
baseProguardMappingFile = "${pathPrefix}/${name}-mapping.txt"
baseResourceRFile = "${pathPrefix}/${name}-R.txt"
}
/**
* 用于用户在代码中判断tinkerPatch是否被使能
*/
android {
defaultConfig {
buildConfigField "boolean", "TINKER_ENABLE", "${tinkerpatchSupport.tinkerEnable}"
}
}
/**
* 一般来说,我们无需对下面的参数做任何的修改
* 对于各参数的详细介绍请参考:
* https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
*/
tinkerPatch {
ignoreWarning = false
useSign = true
dex {
dexMode = "jar"
pattern = ["classes*.dex"]
loader = []
}
lib {
pattern = ["lib/*/*.so"]
}
res {
pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
ignoreChange = []
largeModSize = 100
}
packageConfig {
}
sevenZip {
zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
// path = "/usr/local/bin/7za"
}
buildConfig {
keepDexApply = false
}
}
最后在项目根目录下的Gradle中配置:
ext {
// tinker配置
versionName = "你的app versionName 如:1.0.0"
appKey = "你的Tinker Key"
variantName = "release"
baseInfoName = "最后一次构建的Release基准包文件夹名"
}
当打包时,所构建的基准包会生成在项目根目录下的 infoBak 文件夹中,所以还需自己另行保存最后一次基准包。并且此文件夹不会自动清除,编译多次后会占用非常大空间。。 所以如果不想每次手动删除 infoBak 目录的话,有2种方法,
1.在自定义的 tinkerPatchGradle中加入对Debug模式的判断,如果是Debug则不开启Tinker功能
2.在项目根目录下的Gradle文件中加入以下代码:
...
task clean(type: Delete) {
delete rootProject.buildDir
delete rootProject.file("infoBak") // 这行是要自己加入的
}
...
五、总结
经过GUI打包后,打出的zip包可以直接拖拽进浏览器打开的服务器地址,此外项目中会加入 AndroidToZip.properties 文件,打开文件可以看到
build_pak_type=1
sign_file_name=yourKey
build_pak_type: 0代表是普通Release包,1代表的是Tinker基准包,服务器会根据此值来获取不同目录下的 app-release.apk 文件进行打包处理。
sign_file_name: yourKey就是你输入的签名文件前缀名,服务器会根据此值来判断使用哪一个签名文件进行打包处理。
至此,GUI端功能已实现,请耐心继续看下一篇服务器端的实现。