1. 验证APK的签名
验证APK的签名需要用到下面2个命令:
查看签名文件的属性
keytool -list -keystore 签名文件
F:\mygithub\test1>keytool -list -keystore test1.jks
输入密钥库口令:
密钥库类型: JKS
密钥库提供方: SUN
您的密钥库包含 1 个条目
key0, 2019-10-16, PrivateKeyEntry,
证书指纹 (SHA1): 35:45:24:74:42:D7:3C:81:3C:2F:40:3B:04:E4:4C:DF:65:53:80:3F
查看apk的签名,解压apk,获取 CERT.RSA(位于解压目录下 /META-INF 下)
以下命令行是在 apk 解压目录下执行
keytool -printcert -file META-INF/CERT.RSA
F:\mygithub\test1>keytool -printcert -file CERT.RSA
所有者: CN=test1
发布者: CN=test1
序列号: 42f9a6e7
有效期开始日期: Wed Oct 16 14:37:54 CST 2019, 截止日期: Sun Oct 09 14:37:54 CST 2044
证书指纹:
MD5: CE:F4:41:DC:39:1F:8B:6E:4C:76:E8:99:BC:1A:EB:41
SHA1: 35:45:24:74:42:D7:3C:81:3C:2F:40:3B:04:E4:4C:DF:65:53:80:3F
SHA256: 4A:0B:84:23:C5:12:29:EB:FF:A8:0D:F6:D9:4B:96:D4:CE:80:65:0A:DC:9F:9F:7D:AF:9A:77:DB:23:B9:DD:64
签名算法名称: SHA256withRSA
版本: 3
扩展:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 9E 61 88 DE F6 C2 43 30 A4 DD 75 18 67 D7 E4 86 .a....C0..u.g...
0010: 4C 27 96 FE L'..
]
]
注意问题
网上说debug配置签名无效,配置方式是这样的:
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
shrinkResources true
//多个 flavor ,指定 flavor 使用指定 签名
productFlavors.flavors_default.signingConfig signingConfigs.flavors_default
productFlavors.flavors_dev.signingConfig signingConfigs.flavors_dev
}
//如果 debug 包需要测试诸如微信、地图等第三方 sdk ,则可以指定 debug 包使用 release 包的签名
//debug 并不能设置多个签名
//debug {
// productFlavors.flavors_default.signingConfig signingConfigs.flavors_default
// productFlavors.flavors_dev.signingConfig signingConfigs.flavors_dev
//}
}
是在buildTypes里面配置的,文中指出配置失效的原因是:debug 签名只能指定一个或者使用默认的 debug 签名。
具体没有测试,在这里做一个记录,以备以后查阅。
这里使用另外一种方式是有效的,具体配置方式是不在buildTypes里面配置签名,在productFlavors里面配置,这样debug和release都使用的是自己的签名
buildTypes {
...
debug {
...
signingConfig null // 配置debug模式下的签名,在productFlavors里面配置
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
}
}
productFlavors {
c_vivo {
dimension "company"
}
v_test1 {
signingConfig signingConfigs.test1
dimension "version"
}
}
2. 修改applicationId
往往同一个applicationId的应用在同一台设备只能安装一个,不能同时安装多个,这是因为applicationId是同一个的原因。
我们可以用其 applicationId 属性来实现多个 Apk 安装在同一设备上。
productFlavors {
c_vivo {
dimension "company"
}
v_test1 {
signingConfig signingConfigs.test1
applicationId "com.cold.test1"
dimension "version"
}
v_test2 {
signingConfig signingConfigs.test2
applicationId "com.cold.test2"
dimension "version"
}
}
可以使用aapt查看AndroidManifest.xml的信息
aapt dump xmltree ***.apk AndroidManifest.xml
F:\mygithub\test1>D:\sdk\build-tools\29.0.2\aapt.exe dump xmltree app-c_vivo-v_test2-debug.apk AndroidManifest.xml
N: android=http://schemas.android.com/apk/res/android
E: manifest (line=2)
A: android:versionCode(0x0101021b)=(type 0x10)0x1
A: android:versionName(0x0101021c)="1.0" (Raw: "1.0")
A: android:compileSdkVersion(0x01010572)=(type 0x10)0x1d
A: android:compileSdkVersionCodename(0x01010573)="10" (Raw: "10")
A: package="com.cold.test2" (Raw: "com.cold.test2") // 这个就是替换后的applicationId
A: platformBuildVersionCode=(type 0x10)0x1d
A: platformBuildVersionName=(type 0x10)0xa
E: uses-sdk (line=7)
A: android:minSdkVersion(0x0101020c)=(type 0x10)0xf
A: android:targetSdkVersion(0x01010270)=(type 0x10)0x1d
E: application (line=11)
3. 替换 AndroidManifest.xml 中的属性
可以通过设置占位符来实现动态替换属性值,占位符的使用方法是${},中括号里面的内容表示需要替换的内容。
当AndroidManifest.xml合并的时候,会把占位符的内容设置成build.gradle设置的值。
占位符使用manifestPlaceholders实现。
productFlavors {
c_vivo {
dimension "company"
}
v_test1 {
...
manifestPlaceholders = [NAME : "test1"]
}
v_test2 {
...
manifestPlaceholders = [NAME : "test2"]
}
}
在AndroidManifest.xml 中使用定义的NAME:
<meta-data
android:name="name"
android:value="${NAME}" />
可以通过在程序中验证设置是否正确:
private String readMeta() {
String metaStr = "";
try {
ApplicationInfo applicationInfo =
getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
if (applicationInfo != null && applicationInfo.metaData != null) {
metaStr = (String) applicationInfo.metaData.get("name"); // 这里为对应meta-data的name
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return metaStr;
}
4. 使用不同的资源文件
不同渠道的资源可能会有点出入,例如显示图标,应用名称,启动页面等等。可以在main同级目录下创建以渠道名命名的文件夹,
然后创建资源文件,各文件名称和存放路径需要与main下的一致,否则替换不成功。使用gradle打包的时候会自动替换成渠道下
的资源。这种方法对于不同的源码文件也是有效的,例如在渠道目录下添加自己的源码文件,处理不同的业务逻辑。
gradle合并的资源来自3种来源:
1. 主资源,和main sourceSet相关联,大多位于src/main/res
2. 渠道特有资源Flavor(s).
3. Library项目依赖,通过它们的aar bundle提供资源。
优先级为:Flavor -> main -> Dependencies.
添加完对应的Flavor目录后,切换到不同Variant,对应Variant下的res木有有个黄色方块,表示该目录下的资源会参与编译并合并。
5. 自定义BuildConfig
使用buildConfigField定义BuildConfig
buildConfigField "boolean", "LOG_DEBUG", "false" // 是否输出LOG信息
6. sourceSets配置不同的资源文件
java插件引入了一个概念叫做SourceSets,通过修改SourceSets中的属性,可以指定哪些源文件(或文件夹下的源文件)要被编译,哪些源文件要被排除
android {
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
}
}
例如:
sourceSets {
v_test1 {
java.srcDirs += ['../flavors/v_test1/src'] // 指定源码目录
res.srcDirs += ['../flavors/v_test1/res'] //资源目录
}
...
}
使用 "+=" 会把后面的路径添加到对应的目录集合中,而是用 "=" 会直接覆盖之前设置的路径。例如使用 "+" 设置的app/src/v_test1
这个路径就会失效。
上面配置的目录都属于Flavor这一层级,配置的相关资源不能和其他同为Flavor目录内的资源同名,否则会报Duplicate resources