一、签名文件的生成
打开Android Studio选择Build->Generate Singed Apk,如果有签名文件则选择签名文件路径,如果没有则进行创建create new...进行创建,如下:
在生成签名的过程中需要注意Signature Version V1 V2 两个选项:
- 只勾选V1签名:不会影响什么,但是在7.0上不会使用更安全的验证方式;
- 只勾选V2签名:7.0以下会直接安装完显示未安装,7.0以上则使用了V2的方式验证;
- 同时勾选V1、V2则所有的机型都没有问题。
为了避免忘记了选择某一项可以在app的build.gradle的android标签下加入如下配置,默认帮我们勾选上。
signingConfigs {
debug {
v1SigningEnabled true
v2SigningEnabled true
}
release {
v1SigningEnabled true
v2SigningEnabled true
}
}
二、手动多渠道打包
1、在清单文件中配置meta-data标签信息
<meta-data
android:name="CHANNEL_NAME"
android:value="${CHANNEL_VALUE}"/>
2、在APP的Build.gradle中配置productFlavors渠道
productFlavors {
xiaomi {
manifestPlaceholders = [CHANNEL_VALUE: "xiaomi"]
}
huawei {
manifestPlaceholders = [CHANNEL_VALUE: "huawei"]
}
zhushou360 {
manifestPlaceholders = [CHANNEL_VALUE: "zhushou360"]
}
vivo {
manifestPlaceholders = [CHANNEL_VALUE: "vivo"]
}
oppo {
manifestPlaceholders = [CHANNEL_VALUE: "oppo"]
}
yingyongbao {
manifestPlaceholders = [CHANNEL_VALUE: "yingyongbao"]
}
}
3、打包
选择配置信息里面需要的打包渠道,进行打包:
三、命令行多渠道打包
1、打包信息配置
命令多渠道打包需要在gradle中配置好打包的信息,包括release打包信息和debug打包信息,需要在signingConfigs中进行配置。
signingConfigs {
//release debug配置信息:用于命令打包,图形化界面手动打包不需要此
debug {
keyAlias 'androiddebugkey'
keyPassword 'android'
storeFile file('../debug.keystore')
storePassword 'android'
}
release {
storeFile file("../appkey.jks")
keyAlias "key0"
storePassword "123456"
keyPassword "123456"
- keyAlias:文件别名
- keyPassword、storePassword:密码
- storeFile:文件路径
注意:debug签名文件位于C:\Users\user\.android下面,需要把它复制到项目的根目录下
2、apk包输入目录设定
同样包括包括release打包信息和debug包,在gradle的buildTypes下配置。
buildTypes {
debug {
// 显示Log
buildConfigField "boolean", "LOG_DEBUG", "true"
//混淆
minifyEnabled false
//Zipalign优化
zipAlignEnabled false
// 移除无用的resource文件
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
//签名
signingConfig signingConfigs.debug
// 自定义输出配置
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
// 输出apk名称为
def fileName = "debug_${releaseTime()}_${variant.productFlavors[0].name}_${defaultConfig.versionName}.apk"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
}
release {
// 显示Log
buildConfigField "boolean", "LOG_DEBUG", "false"
//混淆
minifyEnabled true
//Zipalign优化
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名称为
def fileName = "relrese_${releaseTime()}_${variant.productFlavors[0].name}_${defaultConfig.versionName}.apk"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
}
}
3、命令打包
在AndroidStudio窗口左下角打开Terminal面板,输入gradlew assembleRelease命令,会自动完成release打包,输入gradlew assembleDebug命令,会自动完成debug打包,显示:BUILD SUCCESSFUL表明打包完成,可以在outputs/apk里面查看。
debug包
release包
这样打包存在一个弊端,就是把密码和别名写在signingConfigs里面,当提交代码到远程服务器的时候很容易被别人窃取信息,很不安全可以把账户密码等信息写在一个配置文件里面,然后再gradle中获取,这时候当提交代码的时候,把这个文件过滤掉,被人就查看不到了,这里不再进一步的进行说明。
4、注意事项
在命令打包过程中可能出现以下问题:
如果执行命令打包出现以上状况则需要下载gradle,重试一两次,还不行了话建议连接手机移动网络进行下载,亲测可以,如下:
等待下载完毕后就可以愉快的进行命令行打包了。
四、美团多渠道打包方案
直接解压apk,解压后的根目录会有一个META-INF目录,在META-INF目录内添加空文件,通过为不同渠道的应用添加不同名称的空文件,可以唯一标识一个渠道,之后就可以在Java代码中读取空渠道文件名称获取不同渠道名称。
但是在Android7.0之后这中方案不行了,Google为了保证安装包的安全性,在打包的时候,配置了一个Signature V2可选择的打包配置项,该配置能提供更快的应用安装时间、安装的时候会严格的校验完整性,如果有一点变动就是安装失败。美团又开源了新的一款自动化打包工具Walle。
1、原理
新的签名方案中整个APK(ZIP文件格式)会被分为下四个区块,其中区块2保存签名信息是不受保护的,而其他三个是否保护的,区块2中ID-value进行扩展,扩展自定义的渠道id,并保存在APK中,安装apk文件时候是忽略这个区块2的检验的在安装后运行时候可以通过相关代码获取到ID-value的信息,进而进行渠道区分。
2、组成
- 用于写入ID-value信息的Java类库
- Gradle构建插件用来和Android的打包流程进行结合
- 用于读取ID-value信息的Java类库
- 用于供com.android.application使用的读取渠道信息的AAR
3、集成
(1)在位于项目的根目录 build.gradle 文件中添加Walle Gradle插件的依赖, 如下:
buildscript {
dependencies {
classpath 'com.meituan.android.walle:plugin:1.1.6'
}
}
(2)并在当前App的 build.gradle 文件中apply这个插件,并添加上用于读取渠道号的AAR
apply plugin: 'walle'
dependencies {
compile 'com.meituan.android.walle:library:1.1.6'
}
(3)设置渠道包列表:channel
(4)配置插件
walle {
// 指定渠道包的输出路径
apkOutputFolder = new File("${project.buildDir}/outputs/channels");
// 定制渠道包的APK的文件名称
apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk';
// 渠道配置文件
channelFile = new File("${project.getProjectDir()}/channel")
}
(5)输出渠道包
生成渠道包 ./gradlew clean assembleReleaseChannels
如下:
源码地址:https://download.csdn.net/download/yoonerloop/10385608点击打开链接
五、腾讯多渠道打包方案
腾讯多渠道打包方案为VasDolly,原理和美团类似,不再介绍,直接上集成步骤。
1、在根工程的build.gradle中,添加对打包Plugin的依赖:
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0'
classpath 'com.leon.channel:plugin:1.1.7'
}
2、在主App工程的build.gradle中,添加对VasDolly Plugin的引用:
apply plugin: 'channel'
3、在主App工程的build.gradle中,添加读取渠道信息的helper类库依赖:
dependencies {
api 'com.leon.channel:helper:1.1.7'
}
4、配置渠道列表
在gradle.properties文件指定渠道文件名称,该渠道文件必须位于根工程目录下,一行一个渠道信息。
channel_file=channel.txt
5、配置渠道包路径和输出目录
channel{
//指定渠道文件
channelFile = file("/Users/leon/Downloads/testChannel.txt")
//多渠道包的输出目录,默认为new File(project.buildDir,"channel")
baseOutputDir = new File(project.buildDir,"xxx")
//多渠道包的命名规则,默认为:${appName}-${versionName}-${versionCode}-${flavorName}-${buildType}
apkNameFormat ='${appName}-${versionName}-${versionCode}-${flavorName}-${buildType}'
//快速模式:生成渠道包时不进行校验(速度可以提升10倍以上)
isFastMode = false
}
6、输出渠道包
通过gradle channelDebug、gradle channelRelease命令分别生成Debug和Release的多渠道包。如下:
源码地址:https://download.csdn.net/download/yoonerloop/10385708点击打开链接
六、360加固
360加固是一个非常方便简洁的加固工具,无需额外的开发成本,按照步骤上传应用到官网就可以快速完成加固发布,关于360加固的流程在这里不再叙述。需要注意以下几点:
- 加固前必须对apk包进行签名。
- 加固前不需要对代码进行混淆。
- 加固后apk文件包变大。
七、代码混淆
代码混淆的目的就是防止应用被破解、反编译、二次打包、恶意篡改,保护应用数据信息不会被黑客窃取。在打包的配置项中“minifyEnabled true”表示启动代码混淆,在proguard-android.txt文件中配置混淆过滤项。
- 四大组件不能混淆
- support.v4/v7包不混淆
- Serializable实现类、枚举类不被混淆
- 自定义组件不被混淆
- 资源类不被混淆
- 引用的第三方资源jar/SDK/库文件等不能混淆
经过混淆后打包和原来为混淆的debug包进行进行简单的反编译对比,很容易的看出混淆后的代码更加安全,而未混淆的代码就很容易的看到完整的源代码。如下效果图:
需要记住的是反编译只是增加了破解apk的难度,使其破解、反编译、二次打包、恶意篡改的难度增加,由于一些代码在混淆中没有被混淆掉,依然增加被破解的风险,因此在开发中应对重要的数据注意代码的安全加密,打包的时候使用更加安全的加固手段。
最后附上常用的代码混淆内容和build.gralde文件内容:
1、代码混淆内容
#基本指令区
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontpreverify
-verbose
-ignorewarning
-printmapping proguardMapping.txt
-optimizations !code/simplification/cast,!field/*,!class/merging/*
-keepattributes *Annotation*,InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
#默认保留区
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService
-keep class android.support.** {*;}
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep public class * extends android.view.View{
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
-keep class **.R$* {
*;
}
-keepclassmembers class * {
void *(**On*Event);
}
#webview
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
public *;
}
-keepclassmembers class * extends android.webkit.webViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.webViewClient {
public void *(android.webkit.webView, jav.lang.String);
}
#第三方库、jar、SDK
# …………………………………………………………………根据实际项目写……………………………………………………………………………………………
2、build.gralde文件内容
pply plugin: 'com.android.application'
android {
compileSdkVersion 26
buildToolsVersion "26.0.2"
//默认配置信息
defaultConfig {
applicationId "com.channel.channepackage"
minSdkVersion 16
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
//签名配置信息
signingConfigs {
//为了避免忘记了选择Signature Version V1 V2引起安装错误,配置此信息可以一路next
debug {
v1SigningEnabled true
v2SigningEnabled true
}
release {
v1SigningEnabled true
v2SigningEnabled true
}
//release debug配置信息:用于命令打包,手动打包不需要此debug.keystore
debug {
keyAlias 'androiddebugkey'
keyPassword 'android'
storeFile file('../debug.keystore')
storePassword 'android'
}
release {
storeFile file("../appkey.jks")
keyAlias "key0"
storePassword "123456"
keyPassword "123456"
}
}
//打包渠道类型
productFlavors {
xiaomi {
manifestPlaceholders = [CHANNEL_VALUE: "xiaomi"]
}
huawei {
manifestPlaceholders = [CHANNEL_VALUE: "huawei"]
}
zhushou360 {
manifestPlaceholders = [CHANNEL_VALUE: "zhushou360"]
}
vivo {
manifestPlaceholders = [CHANNEL_VALUE: "vivo"]
}
oppo {
manifestPlaceholders = [CHANNEL_VALUE: "oppo"]
}
yingyongbao {
manifestPlaceholders = [CHANNEL_VALUE: "yingyongbao"]
}
}
//打包输出目录
buildTypes {
debug {
// 显示Log
buildConfigField "boolean", "LOG_DEBUG", "true"
//混淆
minifyEnabled false
//Zipalign优化
zipAlignEnabled false
// 移除无用的resource文件
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
//签名
signingConfig signingConfigs.debug
}
release {
// 显示Log
buildConfigField "boolean", "LOG_DEBUG", "false"
//混淆
minifyEnabled true
//Zipalign优化
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名称为
outputFile.name.substring(3,6)
def fileName = buildType.name+"_${releaseTime()}_${variant.productFlavors[0].name}_${defaultConfig.versionName}.apk"
output.outputFile = new File(outputFile.parent,fileName)
}
}
}
}
}
def releaseTime() {
return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:26.+'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12'
}