理解Android studio中点击 run 按钮生成apk没有V1签名问题

3,486 阅读2分钟

背景:最近项目引入了阿里的实名认证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()) {
 
}
  1. 进入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

  1. 最后看看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签名逻辑图