转载请注明出处,原文地址:http://blog.csdn.net/lucherr/article/details/72436018
接触Android Stuidio有一阵子了,之前用的时候有很多小问题,不过现在的版本感觉已经很好用了,所以准备彻底从Eclipse转战Android Stuidio,这段时间把以前经常使用的公用库都从Eclipse移植过来了,今天研究了一下在Android Studio下进行打包签名之类的操作,其实主要是研究Gradle了,以前没有用过Gradle,但是早就耳闻Gradle是非常强大的构建系统,经过一天的奋战,最终实现了通过Gradle脚本来实现打包,签名,自定义文件名,多渠道打包,集成系统签名证书的功能,现在整理成一个demo来记录下实现的要点,以供大家参考和以后查阅。ps:这篇博客是这几天的第三篇博客了,算是弥补下这两年多的空缺:)
开始之前先看看作者的主要开发环境:
System: Ubuntu14.04
Android Studio: V2.3.1
Gradle: V3.3
(一)先看下效果图
在Terminal里进入.config文件夹,执行./build.sh,或者直接从文件管理器里双击build.sh文件,打包任务开始。
然后就交给Gradle了,下图是执行完成时的截图,桌面右上角弹出通知,build/outputs/apk下生成签名并且按照指定规则命名后的apk
(二)build.gradle配置说明
引用自:http://blog.csdn.net/jjwwmlp456/article/details/45057067 在此表示感谢
(三)脚本构建
下面所说的某些脚本AS会自动生成默认配置,某些可以通过File>Project Structure进行添加,然后可以在此基础上进行修改。
1.添加签名脚本,指定签名文件相关配置
签名功能可以使用Build>Generate Signed APK来实现,不过作者追求的是全自动化,完全交给Gradle,提高效率哈哈,多说一句,这里的证书也可以是eclipse下生成的.keystore文件
//签名配置
signingConfigs {
config {
keyAlias 'lucher' //key别名
keyPassword 'lucher' //key密码,最好不要配置在脚本里
storeFile file('/home/lucher/main/sign/lucher.jks') //证书路径,证书最好不要放入项目源码
storePassword 'lucher' //证书密码,最好不要配置在脚本里
}
}
//编译类型
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.config //加入签名配置
}
debug {//debug类型
debuggable true //启用debug的buildType配置
}
custom {//自定义类型
minifyEnabled false
renderscriptDebuggable true
}
}
3.添加产品风格配置脚本,可用于多渠道打包
友盟统计是使用较多的渠道统计工具,按照友盟官方文档说明,渠道信息需要在AndroidManifest.xml中配置如下值:
这个功能可以通过该脚本来配置,为不同渠道定义不同Value(还可以为不同产品配置不同的应用名称,通过manifestPlaceholders = [ APP_NAME : "app-name"]来配置,然后需要把AndroidManifest.xml里application标签下的label改为: android:label="${APP_NAME}",该脚本在示例源码里没有加入)
//产品风格配置,可用于多渠道打包
productFlavors {
//百度推广渠道
baidu {
applicationId "com.lucher.myapplication"
versionCode 1
versionName "1.0"
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
manifestPlaceholders = [ APP_NAME : "app-百度"] //自定义App名称,需要把AndroidManifest.xml里的label改为: android:label="${APP_NAME}"
}
//360推广渠道
qh360 {
applicationId "com.lucher.myapplication"
versionCode 1
versionName "1.0"
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "qh360"]
manifestPlaceholders = [ APP_NAME : "app-360"] //自定义App名称,需要把AndroidManifest.xml里的label改为: android:label="${APP_NAME}"
}
//豌豆荚推广渠道
wandoujia {
applicationId "com.lucher.myapplication"
versionCode 1
versionName "1.0"
manifestPlaceholders = [ APP_NAME : "app-豌豆荚"] //自定义App名称,需要把AndroidManifest.xml里的label改为: android:label="${APP_NAME}"
}
}
4.添加自定义apk名称脚本
有时候需要让apk以指定的规则来命名,可以通过该脚本来实现,本例中命名规则为:
module_flavor-version-time-buildtype.apk
在build.gradle里定义获取当前时间方法(方法定义需要放在android之外)
//获取当前时间
def getCurrentTime() {
return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}
//apk文件重命名
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
def buildType = variant.buildType.name
//这里修改apk文件名,格式为 module_flavor-version-time-buildtype.apk
def fileName = "app_${variant.productFlavors[0].name}-V${defaultConfig.versionName}-${getCurrentTime()}-${buildType}.apk"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
(四)编译命令
注:在使用编译命令之前请确保gradle已经加入环境变量,如果没加入可以通过./gradlew替代gradle(只能在项目根目录下使用)
1编译所有productFlavor及对应所有buildType的apk,相当于按照所有productFlavor和buildType的笛卡尔乘积生成对应的apk,本例中分别有三种productFlavor和三种buildType,生成了9个apk文件
$gradle assemble //仅仅执行项目打包所必须的任务集
$gradle build //执行项目打包所必须的任务集,以及执行自动化测试,所以会较慢
如果当前Project包含多个Module,在Project根目录执行gradle assemble会编译所有的Module
2编译指定productFlavor及buildType的apk
$gradle assemble[productFlavor][buildType]
如果缺失某参数,则会把该参数的所有配置都进行编译,即如果运行gradle assembleflavor,则会编译出flavor所有buildType的apk
例如:
$gradle assemble
$gradle assembleflavorRelease
$gradle assembleflavorDebug
注:gradle支持命令缩写,上面两个命令也可以写成如下格式
$gradle a
$gradle ass
$gradle aR
$gradle assflavorR
$gradle aD
$gradle assflavorD
(五)集成系统签名证书
如果项目需要使用系统权限,则需要在AndroidManifest.xml文件里加入如下属性:
android:sharedUserId="android.uid.system"
加入该属性后apk需要用系统签名才能正常使用。
作者常用的是如下两种方法:
1.android源码环境下签名
需要源码环境,太麻烦
2.对apk进行重新签名
通过下面的命令进行签名
java -jar signapk.jar platform.x509.pem platform.pk8 demo.apk demo_s.apk
这两个方法除了麻烦,还有一个问题:没法直接在开发工具里调试,这是硬伤啊,以前基本都是先打包然后进行重新签名
今天了解到还有另外一个方法可以解决上面的问题,就是把系统签名相关信息导入到已经存在的签名文件里(可以是.jks或者.keystore)
这需要使用一个开源库keytool-importkeypair:https://github.com/getfatday/keytool-importkeypair
使用方法,以lucher.jks为例(需要在linux环境下执行,本例中用到的系统证书是从某系统源码里导出来的,在源码中的路径为:android/build/target/product/security/):
下载keytool-importkeypair,然后把platform.x509.pem platform.pk8 keytool-importkeypair lucher.jks放到同一个文件夹下,执行如下命令即可:
./keytool-importkeypair -k [jks文件名] -p [jks的密码] -pk8 platform.pk8 -cert platform.x509.pem -alias [jks的别名]
本例使用的命令为:
$./keytool-importkeypair -k lucher.jks -p lucher -pk8 platform.pk8 -cert platform.x509.pem -alias lucher
执行成功后会把系统签名信息导入到lucher.jks里,然后签名的方法同上面所说的方法一样,这里不在赘述
最后再强调一点:如果要直接在AS里通过Run运行,需要给Debug模式加上签名信息,或者可以通过左侧的Build Variant选择加入签名配置的模式运行
(六)进阶配置
一,把签名配置从源码移除
如果按照上面所述的方法进行签名配置的话,存在两个问题:
一是密码直接明文显示在gradle里,有严重安全隐患
二是如果多个人同时开发项目,证书的路径各不相同,各自需要修改证书路径.
解决办法:
对于问题一,可以让密码从终端控制行获取,但是这样在打包的时候还需要输入密码,怪麻烦的,跟我追求的完全自动化不符,所以不采用,这种方法可以查询相关资料
对于问题二,我的想法是把签名相关的配置从项目里分离出来,存放到本地配置文件,然后添加一个不需要提交到代码管理工具的配置文件,在该文件里指定分离出来的签名配置文件路径,最后在项目的gradle里加载该配置,这个想法同样可以解决问题一,把密码也写入到这个本地配置文件里
有了这个思路,然后就是研究怎么实现了,经过查阅资料,最后参考下面文章的方法实现了该功能:
https://www.timroes.de/2013/09/22/handling-signing-configs-with-gradle/
值得一提的是:该文章的方法是在gradle.properties里添加的本地签名配置文件路径,不过gradle.properties也加入了版本控制,所以作者使用另一个方法来实现,在项目根目录下新建.config/signgradle.txt文件
在该文件里指定了本地签名配置文件路径,本例为:
/home/lucher/main/sign/sign.gradle
下面说下具体实现步骤:
1.把需要签名的相关配置存放到本地的文件里,然后在项目里加载进来
在项目根目录新建 .config/signgradle.txt文件,内容为签名配置文件地址
/home/lucher/main/sign/sign.gradle
2.sign.gradle加入如下内容
android {
signingConfigs {
release {
storeFile file('/home/lucher/main/sign/lucher.jks') //绝对路径
storePassword "lucher"
keyAlias "lucher"
keyPassword "lucher"
}
}
//编译类型
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
}
//加载签名信息
File configFile = file('../.config/signgradle.txt')
if (configFile.exists()) {
def signGradlePath = configFile.newReader().readLine().trim()
println 'lucher, path:' + signGradlePath
if (file(signGradlePath).exists()) {
apply from: signGradlePath
}
}
5.执行编译打包命令即可
二,编写编译脚本
Linux下编写shell脚本,Windows下编写bat脚本
本例只编写了shell脚本,即工程中.config/build.sh,内容如下:
#快速编译打包apk脚本
echo " **************************打包开始 ************************** "
sleep 1
#执行打包命令前,需要先定位到项目根目录
cd ..
#执行打包命令
gradle a
echo -e "**************************打包完成************************** "
#桌面右上角弹出通知
notify-send build.sh "打包完成!"
然后就可以直接通过./build.sh或者在文件管理器里双击build.sh文件来打包了,非常方便:)
内容有误还望指出
最后给出文中涉及到的资源下载地址,包括示例源码,签名配置信息,keytool-importkeypair(效果图中的app2是打酱油的,为了缩减文件,资源源码中已经去掉)
http://download.csdn.net/detail/lucherr/9845187