最近刚对接了一个新游戏,处理完渠道广告后,市场和运营的同事反馈说在 ViVO 手机上无法安装,系统提示如下:

安装失败截图

既然直接下载安装会失败,那可以借助 ADB 工具安装一下,收到如下报错:

➜   adb install game.apk
  Performing Streamed Install
  adb: failed to install game.apk: Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES: Scanning Failed.: No signature found in package of version 2 or newer for package <package_name>]

意思是说安装包没有 V2 或更高等级的签名。

因为在我的华为手机上是能安装成功的,所以首先可以考虑是 Android 版本问题或 ROM 的问题。

由于 ROM 的独有特征比较难去追踪,因此先看看是否为 Android 版本问题,反馈的两台 ViVO 手机皆为 Android R(API 30,Android 11),而我的华为手机是 Android Q(API 29,Android 10),去官网看看 Android 11 的版本新特性:

Apps that target Android 11 (API level 30) that are currently only signed using APK Signature Scheme v1 must now also be signed using APK Signature Scheme v2 or higher. Users can't install or update apps that are only signed with APK Signature Scheme v1 on devices that run Android 11.

对于以 Android 11(API 30)为目标平台,且目前仅使用 APK 签名方案 v1 签名的应用,现在还必须使用 APK 签名方案 v2 或更高版本进行签名。用户无法在搭载 Android 11 的设备上安装或更新仅通过 APK 签名方案 v1 签名的应用。

简单来说就是,targetSdkVersion30 且仅使用 V1 签名的应用无法在 Android 11 的手机上正常安装。

我遇到的这个问题恰好就是因为这个原因导致的,因为在对接某些渠道的时候,渠道方会要求使用 V1 签名于是我们目前全渠道都使用 V1 签名方案,而这次研发提供的游戏包又恰好 targetSdkVersion30

既然这个问题由两个条件产生的,那就有两个解决方法。

第一种解决方法是使用 V2 或更高版本的签名方案,即使用『ApkSigner』工具进行签名。

使用这个方法也有要注意的地方,官方文档中都有提到:

Apps that target Android 11 (API level 30) or higher can't be installed if they contain a compressed resources.arsc file or if this file is not aligned on a 4-byte boundary.

如果以 Android 11(API 30)或更高版本为目标平台的应用包含压缩的 resources.arsc 文件或者如果此文件未按 4 字节边界对齐,应用将无法安装。

targetSdkVersion30 的应用必须对 APK 文件作对齐优化,在此之前,Android 官方一直都只是建议对齐,并未强制要求。

具体的操作方法在之前的『Android 反编译入门指南』一文中有详细的介绍。

第二种解决方法是将 targetSdkVersion 降为 30 以下。

targetSdkVersion 降级最好由应用的开发者来处理,在游戏行业由于游戏包往往需要经过多个公司发行联运流转,这种情况就会比较麻烦,最好交给上游处理,除非上游不予处理,下游才自行处理。

因为上游可以在工程项目中做调整,有什么问题也能够及时发现,下游只能对现有 APK 文件做修改,很容易出问题。

上游的修改就不多说了,直接在『Android Studio』中处理即可,主要介绍一下下游的处理方案。

依然参考『Android 反编译入门指南』一文,首先对 APK 文件使用『Apktool』进行反编译,然后在 apktool.yml 中修改 targetSdkVersion

sdkInfo:
  minSdkVersion: '16'
  targetSdkVersion: '29'

修改完成后理论上就可以重新打包了,但是我重新打包后发现即使 targetSdkVersion 已经降为 29 了但依然有可能没有解决问题,还需要去 AndroidManifest.xml 中将相关版本信息删除掉,即将:

<manifest ...
    platformBuildVersionCode="1"
    platformBuildVersionName="1.0"
    android:compileSdkVersion="23"
    android:compileSdkVersionCodename="6.0-2438415"
    android:installLocation="auto">
    ...
</manifest>

修改为:

<manifest ...
    platformBuildVersionCode="1"
    platformBuildVersionName="1.0"
    android:installLocation="auto">
    ...
</manifest>

然后回编译,相关版本信息会自动补全,最后使用『JarSigner』或『ApkSigner』工具进行 V1 签名即可。

虽然两种方法都能解决问题,但依然建议使用第一种方案,即使用『ApkSigner』工具进行 V2 签名,毕竟降级只是一个缓兵之计,开倒车就跟不上技术发展的潮流了。

参考内容