关于android数字签名的作用,参见:http://blog.sina.com.cn/s/blog_4a4f9fb50101db1f.html
生成keystore
参见官网签名说明文档:http://developer.android.com/intl/zh-cn/tools/publishing/app-signing.html
在android studio中
build->generate signed APK->create new.按顺序填写之后生成签名所需的keystore文件。
用命令行
keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000
按上图命令分别输入的是签名文件名,别名,指明生成的是2048位RSA秘钥,签名有效期。
我的打包记录
这部分内容参考自:http://www.stormzhang.com/devtools/2015/01/15/android-studio-tutorial6/
android studio 多渠道打包CMD命令:gradle assembleRelease
需要在命令提示行(管理员)中定位到项目位置,然后输入gradle
初始化gradle环境,然后输入如上命令。
我的gradle打包示例一
android studio中build.gradle示例配置如下,将全部渠道名写入channel.txt
文件中,放到app文件夹下面,并将keystore文件放到相同位置。新添加渠道的时候不需要改动build.gradle文件,只需在channel.txt文件中添加新的渠道名就可以了。打包完成之后会生成未签名与签名的两种apk包:
apply plugin: 'com.android.application'
def releaseTime() {
return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}
def keyStore = file('sign.keystore')
android {
compileSdkVersion 22
buildToolsVersion "23.0.0"
defaultConfig {
applicationId "com.zrp.test"
minSdkVersion 9
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
packagingOptions {
exclude 'META-INF/DEPENDENCIES.txt'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/notice.txt'
exclude 'META-INF/license.txt'
exclude 'META-INF/dependencies.txt'
exclude 'META-INF/LGPL2.1'
}
// Remove warnings
lintOptions {
checkReleaseBuilds false
// Or, if you prefer, you can continue to check for errors in release builds,
// but continue the build even when errors are found:
abortOnError false
}
// productFlavors
productFlavors {
def path = "./channel.txt"
file(path).eachLine { channel ->
"$channel" {
manifestPlaceholders = [UMENG_VALUE: channel]
}
}
}
signingConfigs {
app {
storeFile file('sign.keystore')
storePassword project.hasProperty('STOREPASS') ? STOREPASS : '你的秘钥库口令'
keyAlias project.hasProperty('KEYALIAS') ? KEYALIAS : '别名'
keyPassword project.hasProperty('KEYPASS') ? KEYPASS : '秘钥口令'
}
}
buildTypes {
release {
// 不显示Log
buildConfigField "boolean", "LOG_DEBUG", "false"
debuggable false
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
if (keyStore.exists()) {
println "using test key"
signingConfig signingConfigs.app
} else {
println "---------------using default key---------------"
}
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
// 输出apk名称为test_v1.0_2015-01-15_wandoujia.apk
def fileName = "test_v${defaultConfig.versionName}_${releaseTime()}_${variant.productFlavors[0].name}.apk"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:appcompat-v7:22.+'
}
如上gradle文件中,如果未使用签名文件打包,会出现INSTALL_PARSE_FAILED_NO_CERTIFICATES
,或INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION
错误,导致安装失败。所以,一定要使用签名文件进行打包签名,不然用会导致应用安装失败!
我的gradle打包示例二
apply plugin: 'com.android.application'
def releaseTime() {
return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}
android {
compileSdkVersion 22
buildToolsVersion '23.0.0'
defaultConfig {
applicationId "com.zrp.test"
minSdkVersion 9
targetSdkVersion 22
versionCode 1
versionName "1.0"
// dex突破65535的限制
multiDexEnabled true
// 默认是umeng的渠道
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "umeng"]
}
lintOptions {
abortOnError false
}
signingConfigs {
debug {
// No debug config
}
release {
storeFile file("../sign.keystore")
storePassword "秘钥库口令"
keyAlias "别名"
keyPassword "秘钥口令"
}
}
buildTypes {
debug {
// 显示Log
buildConfigField "boolean", "LOG_DEBUG", "true"
versionNameSuffix "-debug"
minifyEnabled false
zipAlignEnabled false
shrinkResources false
signingConfig signingConfigs.debug
}
release {
// 不显示Log
buildConfigField "boolean", "LOG_DEBUG", "false"
minifyEnabled true
zipAlignEnabled true
// 移除无用的resource文件
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
// 输出apk名称为test_v1.0_2015-01-15_wandoujia.apk
def fileName = "test_v${defaultConfig.versionName}_${releaseTime()}_${variant.productFlavors[0].name}.apk"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
}
}
// 友盟多渠道打包
productFlavors {
wandoujia {}
// _360 {}
// baidu {}
// xiaomi {}
// tencent {}
// taobao {}
}
productFlavors.all { flavor ->
flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.+'
}
如果在打包的时候报Unable to compute hash of /../AndroidStudioProjects/../classes.jar
错误,说明在打包混淆的时候需要keep一些文件,让他们不要被混淆。
可以在proguard-rules.pro
文件中添加keep,单独添加的第三方包需要再次添加。
打包完成之后生成的签名包在.\app\build\outputs\apk文件夹下。
美团的打包方案(现阶段最快)
这个方案依赖于google的签名机制,如果google改变android的签名机制的话这个方案就无法使用了。
Github上有人写了这个方法的库:https://github.com/GavinCT/AndroidMultiChannelBuildTool,其博客讲解网址为:http://www.cnblogs.com/ct2011/p/4152323.html
美团的打包原文网址:http://tech.meituan.com/mt-apk-packaging.html
如下为原文摘抄内容:
META-INF
如果能直接修改apk的渠道号,而不需要再重新签名能节省不少打包的时间。幸运的是我们找到了这种方法。直接解压apk,解压后的根目录会有一个META-INF目录,如下图所示:
如果在META-INF目录内添加空文件,可以不用重新签名应用。因此,通过为不同渠道的应用添加不同的空文件,可以唯一标识一个渠道。
下面的python代码用来给apk添加空的渠道文件,渠道名的前缀为mtchannel_:
import zipfile
zipped = zipfile.ZipFile(your_apk, 'a', zipfile.ZIP_DEFLATED)
empty_channel_file = "META-INF/mtchannel_{channel}".format(channel=your_channel)
zipped.write(your_empty_file, empty_channel_file)
添加完空渠道文件后的目录,META-INFO目录多了一个名为mtchannel_meituan的空文件:
接下来就可以在Java代码中读取空渠道文件名了:
public static String getChannel(Context context) {
ApplicationInfo appinfo = context.getApplicationInfo();
String sourceDir = appinfo.sourceDir;
String ret = "";
ZipFile zipfile = null;
try {
zipfile = new ZipFile(sourceDir);
Enumeration<?> entries = zipfile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = ((ZipEntry) entries.nextElement());
String entryName = entry.getName();
if (entryName.startsWith("mtchannel")) {
ret = entryName;
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (zipfile != null) {
try {
zipfile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
String[] split = ret.split("_");
if (split != null && split.length >= 2) {
return ret.substring(split[0].length() + 1);
} else {
return "";
}
}
这样,每打一个渠道包只需复制一个apk,在META-INF中添加一个使用渠道号命名的空文件即可。这种打包方式速度非常快,900多个渠道不到一分钟就能打完。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。