公司现在用的是 ANT 来打包 apk。昨晚学习了一下用命令行使用 Gradle 打包,先总结,以后用得上。
这篇文章,只讲“是什么”、“怎么做”,至于“为什么”这里先不弄清楚,先把打包这个任务完成以后再来说这个。
配置友盟环境
在 AndroidManifest.xml 的 <application> 标签中添加相关信息:APP_KEY 和渠道号。在这里,渠道号只是一个占位符,在打包的阶段可以动态地替换占位符里的信息。
<application
... >
<meta-data
android:name="UMENG_APPKEY"
android:value="@string/UMENG_APPKEY"/>
<meta-data
android:name="UMENG_CHANNEL"
android:value="${UMENG_CHANNEL_ID}"/>
...
</application>
配置签名信息
在 app
的 module 中的 build.gradle
添加下列信息:别名、别名密码、签名文件地址和签名密码
signingConfigs {
release {
keyAlias 'xxx'
keyPassword 'xxx'
storeFile file('C:/Users/Administrator/Desktop/xxx.jks')
storePassword 'xxx'
}
}
设置 buildTypes 的签名配置
因为要打 release 包,所以需要设置 buildTypes 为 release 的签名配置,如下
buildTypes {
release {
zipAlignEnabled true
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release // 加上这句话即可
}
}
配置渠道信息
渠道号是自己定的,比如:wandoujia(豌豆荚)、baidu(百度)、google(Play Store)等等,想取什么名字就取什么名字。不过不要使用数字开头的命名方式。也不要使用 unknow
这样的名字,因为友盟不允许取这个名字。
还记不记得前面讲的渠道号占位符:${UMENG_CHANNEL_ID}
,这里要做的就是动态的替换占位符的内容。配置信息如下:
productFlavors {
baidu {
manifestPlaceholders = [UMENG_CHANNEL_ID: "baidu"]
}
google {
manifestPlaceholders = [UMENG_CHANNEL_ID: "google"]
}
wandoujia {
manifestPlaceholders = [UMENG_CHANNEL_ID: "wandoujia"]
}
xiaomi {
manifestPlaceholders = [UMENG_CHANNEL_ID: "xiaomi"]
}
owen {
manifestPlaceholders = [UMENG_CHANNEL_ID: "owen"]
}
}
执行打包命令
到这里,相关的配置已经全部完成了,可以执行打包命令了,在控制台输入下面这两条命令
gradlew clean
gradlew assembleRelease
打包好的 apk 文件就放在 app module 的 build\outputs\apk
文件夹下
![](https://i-blog.csdnimg.cn/blog_migrate/cd73e188cf49b204bc07cd65077b8551.webp?x-image-process=image/format,png)
完整的 build.gradle 配置
apply plugin: 'com.android.application'
android {
signingConfigs { // 签名配置
release {
keyAlias 'xxx'
keyPassword 'xxx'
storeFile file('C:/Users/Administrator/Desktop/xxx.jks')
storePassword 'xxx'
}
}
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "com.owen.packapk"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release { // buildTypes 配置
zipAlignEnabled true
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
productFlavors { // 渠道配置
baidu {
manifestPlaceholders = [UMENG_CHANNEL_ID: "baidu"]
}
google {
manifestPlaceholders = [UMENG_CHANNEL_ID: "google"]
}
wandoujia {
manifestPlaceholders = [UMENG_CHANNEL_ID: "wandoujia"]
}
xiaomi {
manifestPlaceholders = [UMENG_CHANNEL_ID: "xiaomi"]
}
owen {
manifestPlaceholders = [UMENG_CHANNEL_ID: "owen"]
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
}
以上就打打包的全部内容了,做到这里就可以完成打包任务了。不过还有一些地方可以优化。
隐藏签名信息
签名信息这种东西属于私密的信息,应该保护起来,最好不要明文写到 build.gradle 里,更不要上传到代码仓库中去。应该放在单独的文件中,在 build.gradle 里去引用这个文件读取签名信息。
清空 signingConfigs
signingConfigs {
release {
}
}
在 app module 中新建一个文件 signing.properties
,填入签名信息
STORE_FILE=C:/Users/Administrator/Desktop/XXX.jks
STORE_PASSWORD=XXX
KEY_ALIAS=XXX
KEY_PASSWORD=XXX
在 build.gradle 里添加下列代码去读取这个文件中的签名信息。
File propFile = file('signing.properties');
if (propFile.exists()) {
def Properties props = new Properties()
props.load(new FileInputStream(propFile))
if (props.containsKey('STORE_FILE') && props.containsKey('STORE_PASSWORD') &&
props.containsKey('KEY_ALIAS') && props.containsKey('KEY_PASSWORD')) {
android.signingConfigs.release.storeFile = file(props['STORE_FILE'])
android.signingConfigs.release.storePassword = props['STORE_PASSWORD']
android.signingConfigs.release.keyAlias = props['KEY_ALIAS']
android.signingConfigs.release.keyPassword = props['KEY_PASSWORD']
} else {
android.buildTypes.release.signingConfig = null
}
} else {
android.buildTypes.release.signingConfig = null
}
signing.properties
这个文件不能上传到代码仓库里去,所以要在 .gitignore
文件中添加一句话
signing.properties
经过这几个步骤,就实现了保护签名信息的需求。
简化渠道号配置信息的写法
之前的渠道信息是这么写的
productFlavors {
baidu {
manifestPlaceholders = [UMENG_CHANNEL_ID: "baidu"]
}
google {
manifestPlaceholders = [UMENG_CHANNEL_ID: "google"]
}
wandoujia {
manifestPlaceholders = [UMENG_CHANNEL_ID: "wandoujia"]
}
xiaomi {
manifestPlaceholders = [UMENG_CHANNEL_ID: "xiaomi"]
}
owen {
manifestPlaceholders = [UMENG_CHANNEL_ID: "owen"]
}
}
可以发现这些配置信息的写法都差不多,有一定规律。所以,还可以简写成这样
productFlavors {
baidu {}
google {}
wandoujia {}
xiaomi {}
owen {}
}
productFlavors.all { flavor ->
flavor.manifestPlaceholders = [UMENG_CHANNEL_ID: flavor.name]
}
跟踪打包过程
建议在命令后面加上 --stacktrace
参数,这样万一命令执行失败,可以看到详细的出错信息
gradlew clean --stacktrace
gradlew assembleRelease --stacktrace
定制 APK 的文件名
默认情况下,打包好的 APK 的文件名是这样的
![](https://i-blog.csdnimg.cn/blog_migrate/cd73e188cf49b204bc07cd65077b8551.webp?x-image-process=image/format,png)
我想要的这样的文件名:app_版本名称_版本号_日期_渠道号.apk
,可以添加如下信息
def packageTime() {
return new Date().format("yyyyMMdd", TimeZone.getTimeZone("UTC"))
}
android {
applicationVariants.all { variant ->
variant.outputs.each { output ->
// 重命名产出的apk文件
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
File outputDirectory = new File(outputFile.parent);
def fileName
if (variant.buildType.name == "release") {
fileName = "app_v${defaultConfig.versionName}_${defaultConfig.versionCode}_${packageTime()}_${variant.productFlavors[0].name}.apk"
} else {
fileName = "app_v${defaultConfig.versionName}_${defaultConfig.versionCode}_${packageTime()}_debug.apk"
}
output.outputFile = new File(outputDirectory, fileName)
}
}
}
}
打包结果如图:
![](https://i-blog.csdnimg.cn/blog_migrate/1aa8d0d4006a807684a922e2e9293850.webp?x-image-process=image/format,png)
删除冗余的 unaligned apk 文件
在 app/build/outputs/apk 文件夹中,可以看到一些文件名末尾带有 unaligned
文字的冗余文件,这些文件没有用,可以删掉,只需要在 build.gradle 中添加下列命令就好了
applicationVariants.all { variant ->
variant.outputs.each { output ->
// 重命名产出的apk文件
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
File outputDirectory = new File(outputFile.parent);
def fileName
if (variant.buildType.name == "release") {
fileName = "app_v${defaultConfig.versionName}_${defaultConfig.versionCode}_${packageTime()}_${variant.productFlavors[0].name}.apk"
} else {
fileName = "app_v${defaultConfig.versionName}_${defaultConfig.versionCode}_${packageTime()}_debug.apk"
}
output.outputFile = new File(outputDirectory, fileName)
}
// 删除unaligned apk
if (output.zipAlign != null) {
output.zipAlign.doLast {
output.zipAlign.inputFile.delete()
}
}
}
}
打包之后,就没有这些冗余文件了
![](https://i-blog.csdnimg.cn/blog_migrate/858542386a91c84451e3a6db53c2e54b.webp?x-image-process=image/format,png)
Github
完整项目地址在这里:https://github.com/owenmike/packApk
总结
多渠道打包的步骤:
- 配置 AndroidManifest.xml
- 配置签名信息
- 配置渠道号信息
- 定制与优化
原文链接:http://www.jianshu.com/p/7236ceca2630
这种打包其实很耗时,当你的渠道超过10个的时候估计很麻烦,其实有更快的方法:
http://www.jianshu.com/p/e0783783d26d
程序猴: @cyning 美团方案确实不错,不过这个方案还需要注意一点,需要重新执行一下zipAligned才比较合适