先看结论与判断条件

  • SDK 边界必须从准备发布的最终 APK 二进制 Manifest 读取,Gradle 配置和源码清单只能说明构建意图。
  • minSdk 变化主要改变安装资格与最低系统覆盖,targetSdk 变化主要改变平台对应用采用的兼容与安全行为。
  • 同一仓库的不同 build type、product flavor、source set 和 applicationId 组合可能生成不同边界,必须逐发布变体验收。
  • targetSdk 不变也不能跳过新系统回归,因为平台存在面向所有应用生效的行为变化。
  • 差分门禁要将 SDK 变化、包名、versionCode、签名责任和真实升级路径绑定,避免比较两个无关候选。
  • 静态差异负责生成风险问题和测试矩阵,最终放行依赖目标设备上的安装、升级、组件、权限和核心业务回执。

先区分安装边界与运行语义

minSdk 与 targetSdk 都写在应用的 SDK 约束中,但它们回答不同问题。minSdk 用于表达应用声明的最低平台要求,变化会直接改变哪些系统版本具备安装资格;targetSdk 表示应用已经面向哪个平台行为级别完成适配,系统会据此决定部分兼容和安全行为。把两者合并成一个“SDK 升级”告警,会让测试人员不知道该补最低版本安装,还是该审查权限、窗口、调度和组件语义。

发布门禁的输入必须来自前后两个最终 APK,而不是 `build.gradle` 中的默认值。Manifest 会由主工程、依赖和变体 source set 合并,CI 也可能选择不同任务或属性。应从二进制 Manifest 提取 minSdk、targetSdk、applicationId、versionCode 和版本名,再绑定文件摘要、variant、签名责任及生成时间。若任一候选身份不完整,差分没有可靠比较基础,应先阻断而不是用仓库配置补值。

本文只回答 minSdk 与 targetSdk 变化如何形成差分门禁,不写成一般的 Android 新版本适配清单,也不重复 AAB 与 split APK 的发布身份问题。静态值变化可以决定检查方向,不能证明某项平台行为一定被应用触发。没有真实候选、功能清单和设备回执时,只能报告边界变化与待验证项,不能宣称兼容通过或全部用户不受影响。

minSdk 与 targetSdk 的门禁责任
字段直接改变静态检查设备验证
minSdk 上调最低安装系统范围收窄前后值与发布策略旧边界和新边界安装结果
minSdk 下调声明支持范围扩大依赖 API 与兼容分支新增低版本功能矩阵
targetSdk 上调部分平台兼容行为切换跨越行为级别与功能映射目标系统组件和权限回归
targetSdk 下调发布配置异常或策略回退来源、渠道与允许策略不得以设备通过掩盖配置错误
字段不变SDK 边界未变化候选身份与清单摘要仍覆盖面向所有应用的变化
字段缺失无法形成可靠边界提取器与候选完整性不进入设备放行阶段

从最终 Manifest 固定比较对象

Android Manifest 定义组件、权限、intent filter、SDK 约束和应用元数据。差分工具应读取最终二进制清单的 `<uses-sdk>` 结果,并同时盘点可能受 target 行为影响的组件、权限和元数据。只打印两个数字会丢失解释上下文,例如目标级别上调后,应用是否使用对应权限、后台任务、窗口模式或导出组件,决定了测试优先级。门禁需要把 SDK 差异关联到真实功能,不是机械生成整个平台文档。

比较前要验证两个 APK 确实属于连续发布链。applicationId 应一致,versionCode 应符合目标渠道的升级条件,签名需要由独立流程验证兼容或具备轮换证明。若包名不同,它们可能是两个产品或环境包;若版本码方向异常,所谓“新候选”可能拿错。SDK 差异脚本可以提示这些身份字段,但不能替代签名验证,也不能根据文件名猜测先后顺序。

提取回执要保留工具版本和原始字段,避免解析器升级造成静默变化。部分旧包可能没有显式声明某个值,工具会展示平台或构建默认行为;门禁不能把默认值与显式值混为一谈,应记录值的来源和是否显式。若无法从最终产物取得确定值,结果应标为不可比较,返回构建链查明,而不是为了让报告完整而填入当前项目配置。

  • 前后 APK 均保存 SHA-256 摘要和提取工具版本
  • applicationId、versionCode、版本名和 variant 同时读取
  • minSdk 与 targetSdk 标明显式值或解析来源
  • 签名兼容性由独立发布身份门禁验证
  • 组件、权限和元数据差异与 target 变化关联
  • 不可比较字段不会被源码默认值静默补齐

minSdk 上调与下调需要完全不同的测试

minSdk 上调意味着旧于新下界的设备不再属于声明支持范围。发布决策要先回答这是计划中的用户范围调整,还是依赖升级、变体串用或构建配置意外带来的结果。静态门禁应核对产品支持策略、商店渠道和依赖最低要求,并在设备矩阵中保留旧 minSdk、旧下界附近版本、新 minSdk 和一个较新版本。真正的影响还需要当前安装基础与渠道数据,不能从 APK 数字推断用户数量。

minSdk 下调看似扩大覆盖,风险却常被低估。源码可能调用了高版本 API,依赖可能只在较新系统经过验证,资源限定、加密提供者、文件系统和后台限制也可能不同。门禁应要求新增低版本设备上的安装、冷启动、核心页面、网络、存储、权限、升级和异常恢复测试,并检查 API 守卫与替代路径。构建成功只说明字节可产出,不说明低版本运行时能够安全执行。

minSdk 不变也要关注依赖和构建工具的最低运行假设。某个 SDK 可能保留相同 Manifest 下界,却在特定路径调用更高 API;R8 或 desugaring 配置变化也可能改变低版本行为。差分报告应把 minSdk 数字与依赖图、核心功能 API 使用和设备失败记录并列。本文不宣称静态工具可以证明所有 API 调用有正确守卫,最终仍需要目标设备或等价运行证据。

minSdk 变化的设备矩阵生成规则
变化方向最低必测位置主要问题放行证据
上调旧下界与新下界旧设备是否按计划退出支持产品决策和安装回执
上调新下界的真实设备首次安装与核心功能是否可用安装、启动和业务用例
下调新增最低系统API 守卫和依赖是否兼容低版本完整回归
下调新增范围内中间版本兼容分支是否覆盖代表设备和失败路径
不变原最低支持系统依赖与工具链是否改变假设基线设备回归
来源不明暂不进入矩阵是否拿错 variant 或候选先恢复可追溯提取

targetSdk 上调要按行为边界筛选功能

targetSdk 上调不是把一个数字改大后重新编译。平台会针对达到特定目标级别的应用启用一组兼容、安全、权限、调度或界面行为,实际风险取决于应用使用了哪些能力。以 Android 16 为例,面向 targetSdk 36 的行为文档覆盖大屏、权限、调度和安全等方面。门禁应记录跨越了哪些目标级别,再将官方变化条目映射到应用组件和业务路径,不能把整页列表无差别复制成测试计划。

映射需要明确“平台事实”和“项目判断”。官方资料可以说明某项行为在什么条件下生效,项目清单和代码可以说明应用是否可能使用相关 API,只有设备用例才能说明候选在目标场景的结果。报告应为每项保留适用条件、触发组件、验证步骤、预期结果和未覆盖限制。若项目没有使用某能力,可以标记不适用,但要保存判断依据,不能仅凭负责人印象删除。

targetSdk 下调通常不是普通兼容策略,而是发布配置、渠道任务或回退决策需要审查的信号。即使某个设备测试暂时通过,也不能证明下调符合商店政策和安全基线。门禁应要求解释来源、审批和后续计划,并核对是否选择了错误 variant。本文不提供某个渠道当前 target 要求的时效性结论,具体政策需要在发布时以目标渠道官方要求为准。

  • 列出前后 targetSdk 跨越的每个行为级别
  • 官方行为条目映射到具体组件和业务功能
  • 每项记录生效条件、测试步骤和预期结果
  • 不适用结论保存代码或配置依据
  • targetSdk 下调触发配置来源和发布策略审查
  • 渠道政策在实际发布日期单独核对

新系统回归不能只盯着 targetSdk 变化

平台还存在面向所有应用生效的行为变化,因此 targetSdk 未变并不等于新系统无需测试。Android 16 面向所有应用的行为文档说明,旧目标版本也可能受到部分系统变化影响。差分门禁应把“候选 target 变化触发的测试”和“目标操作系统本身触发的测试”分成两条轴。否则 target 不变的维护版本容易漏掉新系统问题,target 上调时又可能把所有失败错误归因于目标级别。

设备矩阵至少覆盖最低支持系统、主要使用区间、跨越的 target 行为边界和计划支持的最新系统。具体设备和厂商范围应依据真实用户与风险选择,不能用一个模拟器代表全部环境。对依赖组件、系统权限、后台执行、通知、文件访问、窗口和网络等行为,要从应用功能清单选取真实入口。没有使用的能力无需制造测试,但适用性判断必须可复核。

失败归因需要对照组。可以在同一系统上比较前后 APK,也可以在同一 APK 上比较不同系统,但要保持签名、数据状态、账号、网络和测试步骤一致。若同时更换 targetSdk、依赖、加固配置和业务代码,仅凭一次失败无法确定根因。门禁应把变化集合写入报告,优先用最小差分候选或可控开关定位,不应把平台文档中的某条变化直接当作已证实根因。

target 与系统版本的双轴回归
target 变化系统变化测试重点结论边界
不变不变候选自身回归只能覆盖代码与配置变化
上调不变目标级别触发行为按实际功能筛选
不变升级面向所有应用的系统变化不能归因于 target
上调升级两类变化交叉需要对照候选拆分根因
下调任意发布配置和策略异常设备通过不等于允许发布
未知任意候选身份或提取失败停止生成兼容结论

更新连续性字段要和 SDK 差异同时绑定

Android 应用更新要求包名保持一致、签名兼容或具备合法轮换证明,并使用可接受的 versionCode。SDK 边界差分应把这些身份字段一起保存,因为只有同一更新链中的前后候选,minSdk 和 targetSdk 比较才有业务意义。包名不同可能是独立安装的渠道包,签名不兼容可能导致无法覆盖安装,versionCode 异常则可能让新候选无法成为更新。SDK 测试不能绕过这些前置事实。

但更新条件不等于业务层升级安全。即使系统允许覆盖安装,minSdk 上调后的旧设备策略、target 行为变化下的数据迁移、权限状态、通知设置和后台任务仍需验证。反过来,SDK 数字没有变化也不能证明升级连续性,因为签名、包名、组件和数据结构可能发生其他变化。本文只将身份字段作为差分对象绑定条件,不形成第二套 APK 更新连续性指南。

测试前应设计两条路径:干净安装用于验证当前边界和首次运行,真实旧版升级用于验证数据、权限和任务状态。若 minSdk 上调,旧系统上的已安装版本如何继续服务或退出支持,需要产品和渠道决策;若 targetSdk 上调,则同一设备上前后版本的权限与系统行为对比更有解释力。所有用例必须记录安装前状态和最终 APK 摘要,避免把残留数据当作候选差异。

  • 前后候选 applicationId 一致且更新关系明确
  • versionCode 方向和目标渠道要求相符
  • 签名兼容或轮换证明由独立门禁确认
  • 干净安装和真实旧版升级分别测试
  • 权限、数据和后台任务状态在升级前后记录
  • 更新身份条件不替代 SDK 行为回归

用差异脚本生成最小回归任务

下面的 Python 示例读取前后两个 APK 的脱敏元数据 JSON,字段来自最终二进制 Manifest 和独立身份提取。脚本验证包名、variant、versionCode、minSdk 与 targetSdk 的完整性,根据变化方向输出必须执行的回归任务。它不解析签名私钥、不修改 APK,也不声称设备测试通过;输出只是测试计划输入,真正结果要由设备和用例回执补充。

输入必须与候选摘要绑定,并由同一版本的提取工具生成。示例对缺失文件、非整数 SDK、包名不一致、variant 不一致、versionCode 未增加和 SDK 值异常设置可达失败路径。targetSdk 下调会被标为阻断,minSdk 上调或下调则生成不同安装边界任务。对 targetSdk 上调,脚本要求评审跨越行为级别和最新系统;它不会内置某个平台文档的全部变化,避免规则过期。

生产门禁可将官方行为清单按 target 级别版本化,再由功能所有者映射到组件和用例。生成器应输出任务键、触发差异、所需设备、证据字段和责任人,不能自动把未执行任务标成通过。若同一批次还改变依赖或加固配置,应把变化摘要附在任务中,为失败归因保留上下文。脚本通过只说明元数据差异可解释,不代表发布完成。

比较前后 APK 的 SDK 边界并生成回归项
from pathlib import Path
import json
import sys

if len(sys.argv) != 3:
    raise SystemExit("usage: sdk_diff.py before.json after.json")

def load(path_text):
    path = Path(path_text).resolve()
    if not path.is_file():
        raise SystemExit(f"input missing: {path.name}")
    data = json.loads(path.read_text(encoding="utf-8"))
    for key in ("apk_sha256", "application_id", "variant"):
        if not str(data.get(key) or "").strip():
            raise SystemExit(f"required field missing: {key}")
    for key in ("version_code", "min_sdk", "target_sdk"):
        if not isinstance(data.get(key), int) or data[key] < 1:
            raise SystemExit(f"invalid integer field: {key}")
    return data

before = load(sys.argv[1])
after = load(sys.argv[2])
if before["application_id"] != after["application_id"]:
    raise SystemExit("application_id differs; packages are not comparable updates")
if before["variant"] != after["variant"]:
    raise SystemExit("variant differs; compare equivalent release artifacts")
if after["version_code"] <= before["version_code"]:
    raise SystemExit("version_code did not increase")

tasks = []
def add(kind, reason, checks):
    tasks.append({"kind": kind, "reason": reason, "checks": checks})

old_min, new_min = before["min_sdk"], after["min_sdk"]
old_target, new_target = before["target_sdk"], after["target_sdk"]
if new_min > old_min:
    add("min-sdk-raised", f"{old_min} -> {new_min}", ["support-policy-review", "old-boundary-install", "new-boundary-install"])
elif new_min < old_min:
    add("min-sdk-lowered", f"{old_min} -> {new_min}", ["new-minimum-device", "api-guard-review", "dependency-compatibility"])
else:
    add("min-sdk-unchanged", str(new_min), ["minimum-supported-device-baseline"])

if new_target > old_target:
    add("target-sdk-raised", f"{old_target} -> {new_target}", ["crossed-behavior-review", "permission-and-component-tests", "latest-os-test"])
elif new_target < old_target:
    raise SystemExit("target_sdk decreased; release policy review required")
else:
    add("target-sdk-unchanged", str(new_target), ["all-app-os-changes-review"])

report = {"before_sha256": before["apk_sha256"], "after_sha256": after["apk_sha256"], "application_id": after["application_id"], "variant": after["variant"], "tasks": tasks}
print(json.dumps(report, ensure_ascii=False, indent=2))

以设备回执和明确边界完成放行

依赖真实 Android 运行时、组件和系统 API 的语义应通过设备端 instrumented test 验证。差分门禁生成矩阵后,每项回执要绑定设备型号、系统版本、ABI、安装方式、前后 APK 摘要、账号与数据状态、步骤、断言和日志时间。最低系统只跑启动页不够,target 行为也不能只看应用未崩溃;测试必须进入真实组件、权限、后台任务、窗口和核心业务路径。

放行记录要区分静态事实、项目判断和设备结果。静态事实包括最终 SDK 值与候选身份;项目判断包括哪些平台变化适用、哪些设备代表用户范围;设备结果只覆盖实际执行的环境和用例。一个设备通过不能代表所有 API、ABI 和厂商矩阵,一个官方行为列表也不能证明项目触发了全部变化。任何未覆盖项要显式保留,不得用“风险可控”替代证据。

完整证据包应包含前后 APK 摘要、Manifest 提取、variant、包名、versionCode、签名门禁引用、SDK 差异、行为映射、设备矩阵、失败与豁免。需要评估实际候选时,可通过御盾中央平台提交脱敏的前后摘要、最终 Manifest 字段、发布变体和现有设备范围,先生成差分问题,再执行针对性回归。没有真实设备或项目数据时,本文不登记任何兼容通过结论。

  • 每条设备回执绑定前后候选摘要和安装状态
  • 最低系统、目标行为边界和最新系统均有代表用例
  • 组件、权限、后台任务和核心业务使用真实入口
  • 静态事实、适用性判断和设备结果分层登记
  • 未覆盖设备与功能明确列入发布限制
  • 失败归因保留依赖、配置和业务代码变化上下文

事实依据与适用边界

以下内容区分官方事实、本文工程判断和不能外推的范围,避免把设计建议写成未经验证的产品结论。

本文判断事实或工程依据适用限制
Manifest 定义应用组件、权限、intent filter、SDK 约束和应用元数据。Android app manifest 说明应用清单的核心声明范围。静态 Manifest 不能证明运行时组件、权限和业务授权行为正确。
build type、product flavor、source set、applicationId 和签名配置会组合成不同发布变体。Android build variants 说明构建类型、产品风味、source set 和应用标识的变体机制。同一代码仓库不代表所有 variant 具有相同 SDK、资源、证书和运行行为。
面向 targetSdk 36 的应用会遇到 Android 16 大屏、权限、调度和安全等行为变化。Android 16 target behavior changes 列出面向 Android 16 目标级别的行为变化。文档条目会更新,必须按实际 targetSdk、组件和功能筛选,不能假定全部适用。
Android 16 存在不依赖 targetSdk、面向所有应用生效的行为变化。Android 16 all-app behavior changes 列出面向所有应用的系统行为变化。平台变化不意味着每个候选都会触发同一问题,仍需目标功能回归。
Android 应用更新需要保持 applicationId、兼容签名或轮换证明,并使用可接受的 versionCode。How Android app updates work 说明 Android 应用覆盖更新的身份条件。平台更新条件不等于业务数据迁移、权限状态和防重放策略已经正确。
依赖真实 Android 运行时、组件和系统 API 的语义应通过设备端测试验证。Android instrumented tests 说明 instrumented test 适用于需要真实 Android 环境的行为。单一设备通过不能代表完整 API、ABI 和厂商矩阵。
minSdk 与 targetSdk 差异应触发不同的发布问题和设备矩阵。工程判断:安装资格变化和平台兼容行为变化需要不同证据,合并告警会掩盖测试责任。差分规则不能预测实际用户影响,也不能替代渠道数据和设备结果。
SDK 差分必须绑定前后最终 APK 的身份、variant 和版本信息。工程判断:只有同一发布链的可追溯候选才具备可解释的边界比较关系。字段一致不能替代签名验证,也不能证明应用内升级和数据迁移安全。

工程常见问题

只比较 build.gradle 中的 minSdk 和 targetSdk 可以吗?

不可以。变体、source set 和 Manifest 合并会改变最终值,发布门禁必须读取前后最终 APK 的二进制 Manifest。

minSdk 上调后只测试新的最低系统够吗?

不够。还要确认旧支持范围的产品与渠道决策,并覆盖新下界、主要系统区间、升级路径和核心功能。

minSdk 下调为什么风险可能更大?

它扩大了声明支持范围,低版本 API、依赖、资源、权限和系统服务差异都需要新增设备回归,构建成功不能证明运行兼容。

targetSdk 没变化是否可以跳过最新 Android 回归?

不可以。平台存在面向所有应用生效的行为变化,旧目标版本的应用仍要测试真实功能入口。

targetSdk 上调后是否要测试官方文档中的每一项?

应先按应用组件和功能筛选,保存不适用依据;适用项再建立设备用例,不能无差别复制列表或凭印象删除。

准备 SDK 边界差分需要哪些材料?

准备前后最终 APK、摘要、Manifest 提取、variant、包名、versionCode、签名门禁引用、功能清单、目标渠道和设备范围。

想用自己的 App 验证?

提交候选包、目标系统和关键业务路径,申请御盾 PoC 与兼容性评估。

继续阅读: APK、DEX 与签名完整性如何一起验收