關於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多個渠道不到一分鐘就能打完。