背景:最近项目引入了阿里的实名认证sdk,在运行app后走到sdk初始化代码后因为v1签名校验失败而崩溃。测试发现直接点击Android studio run按钮生成的apk包含V2签名不包含V1签名,然而通过gradle task(比如assemble(VariantName),install(VariantName))生成的apk包含V1和V2签名,那么为什么直接直接run生成的apk无V1签名呢,下面探究一下。
先列一下app 的 build.gradle 签名相关配置:如下
signingConfigs {
config {
keyAlias 'xxx'
keyPassword 'xxx'
storeFile file('xxx')
storePassword 'xxx'
v1SigningEnabled true
v2SigningEnabled true
}
}
buildTypes {
release {
...
signingConfig signingConfigs.config
...
}
debug {
...
signingConfig signingConfigs.config
...
}
}
分析思路: android通过gradle的task链打包,链条上每个task负责具体一项功能(比如compileDebugJavaWithJavac这个task内部封装了javac的调用,把java编译成class),那么签名功能肯定是放在打包的task中或者打包task之后的某个task中,可以调试android gradle plugin分析。 注: gradle.propety中配置:org.gradle.jvmargs=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005 可以开启全局调试,这样在IDE调用gradle时候就能调试了(比如在run或sync时候)
1。通过搜索调试发现签名相关逻辑在 PackageAndroidArtifact类中(对应package(VariantName) task),PackageAndroidArtifact有个doTask静态方法 其中和sign相关的代码如下
try (IncrementalPackager packager =
new IncrementalPackagerBuilder(params.apkFormat, params.packagerMode)
.withOutputFile(outputFile)
.withSigning(
SigningConfigMetadata.Companion.load(params.signingConfig),
params.minSdkVersion,
params.targetApi)
.withCreatedBy(params.createdBy)
// TODO: allow extra metadata to be saved in the split scope to avoid
// reparsing
// these manifest files.
.withNativeLibraryPackagingMode(
PackagingUtils.getNativeLibrariesLibrariesPackagingMode(manifest))
.withNoCompressPredicate(
PackagingUtils.getNoCompressPredicate(
params.aaptOptionsNoCompress, manifest))
.withIntermediateDir(incrementalDirForSplit)
.withKeepTimestampsInApk(params.keepTimestampsInApk)
.withDebuggableBuild(params.isDebuggableBuild)
.withAcceptedAbis(
filter == null ? params.abiFilters : ImmutableSet.of(filter))
.withJniDebuggableBuild(params.isJniDebuggableBuild)
.build()) {
}
- 进入IncrementalPackagerBuilder.withSigning方法,其中有两处最终确定了apk包是否使用v1或v2签名,如下
boolean enableV1Signing =
enableV1Signing(
signingConfig.isV1SigningEnabled(),
signingConfig.isV2SigningEnabled(),
minSdk,
targetApi);
boolean enableV2Signing =
(targetApi == null || targetApi >= NO_V1_SDK)
&& signingConfig.isV2SigningEnabled();
其中 NO_V1_SDK = 24,minSdk指的是build.gradle中指定的minsdk,targetApi是连接电脑的手机或者模拟器系统版本号, 在没有连接手机或者不走IDE直接终端执行task时targetApi为null(targetApi是SplitterParams的一个字段,SplitterParams是由Android studio注入的, 里面包含了很多环境信息,具体可以自己调试看,至于Android studio在哪里注入的可以去研究下Android studio源码:github.com/JetBrains/a… 反正我是没兴趣研究,因为有兴趣也看不懂,看懂了用处也不大)
因为build.gradle中设置了
v1SigningEnabled true
v2SigningEnabled true
所以上面代码 signingConfig.isV1SigningEnabled() 和 signingConfig.isV2SigningEnabled() 都是true
- 最后看看IncrementalPackagerBuilder.enableV1Signing方法:
/**
* This method has a decision logic on whether to sign with v1 signature or not. Even if we have
* v1 signature specified it might be useless if the target or minSdk version is high enough and
* we sign with v2 since in that case only v2 is checked.
*
* @param v1Enabled if v1 signature is enabled by the user
* @param v2Enabled if v2 signature is enabled by the user
* @param minSdk the minimum SDK
* @param targetApi optional injected target Api
* @return if we actually sign with v1 signature
*/
@VisibleForTesting
static boolean enableV1Signing(boolean v1Enabled, boolean v2Enabled, int minSdk,
@Nullable Integer targetApi) {
if (!v1Enabled) {
return false;
}
// If there is no v2 signature specified we have to sign with v1 even if the versions are
// high enough otherwise we would not have signed at all
if (!v2Enabled) {
return true;
}
// Case where both v1Enabled==true and v2Enabled==true
return (targetApi == null || targetApi < NO_V1_SDK) && minSdk < NO_V1_SDK;
}
一切都清楚了~
三 总结:是否使用v1签名逻辑图