关于android数字签名的做用,参见:http://blog.sina.com.cn/s/blog_4a4f9fb50101db1f.htmlhtml
参见官网签名说明文档:http://developer.android.com/intl/zh-cn/tools/publishing/app-signing.htmlpython
build->generate signed APK->create new.按顺序填写以后生成签名所需的keystore文件。android
keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000git
按上图命令分别输入的是签名文件名,别名,指明生成的是2048位RSA秘钥,签名有效期。github
这部份内容参考自:http://www.stormzhang.com/devtools/2015/01/15/android-studio-tutorial6/android-studio
android studio 多渠道打包CMD命令:gradle assembleRelease
须要在命令提示行(管理员)中定位到项目位置,而后输入gradle
初始化gradle环境,而后输入如上命令。app
android studio中build.gradle示例配置以下,将所有渠道名写入channel.txt
文件中,放到app文件夹下面,并将keystore文件放到相同位置。新添加渠道的时候不须要改动build.gradle文件,只需在channel.txt文件中添加新的渠道名就能够了。打包完成以后会生成未签名与签名的两种apk包:gradle
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
错误,致使安装失败。因此,必定要使用签名文件进行打包签名,否则用会致使应用安装失败!ui
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,单独添加的第三方包须要再次添加。google
打包完成以后生成的签名包在.\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多个渠道不到一分钟就能打完。