Google Play 拒审原因:上传前就能在 APK/AAB 里查出来的那些
本页目录
结论先行:Google Play 拒审,很多时候跟截图、描述没关系,问题就出在发布包本身:targetSdkVersion 低于当前要求、只有 32 位原生库、打的是可调试包、用调试证书签名,或者申请了 READ_SMS 这类受限权限却没填声明。这些全都能在上传前从 APK/AAB 里读出来,也都能在自己的构建里改掉。
本文先构建一个带着常见问题的 App Bundle,交给本站的 Google Play 上架合规检查 跑一遍,逐条修完再跑一遍。下面的输出都来自 2026-10-14 的实际运行。检查在浏览器里完成,文件不会上传。
适合谁读:在 Play Console 收到 “Your app is not compliant with Google Play policies”、更新被拒的 Android 开发者,以及想在提交前就把问题排掉的人。
读完你会知道:
- 哪些拒审原因在安装包里,哪些在 Play Console 里
- 每项检查(规则编号、级别、触发条件)查什么、怎么改
- 怎么找出是哪个库带进来的权限
- 被拒之后怎么重新提交、什么时候该申诉
只分析你自己构建的应用,或已获授权测试的应用。
拒审分两类
- 商店信息和各类声明:隐私政策、数据安全表单、内容分级、广告声明、给审核人员的登录信息。这些从文件里看不到,Google 的 Prepare your app for review 有完整清单。
- 发布包里写死的东西:目标 API 级别、ABI、Manifest 标志、权限、服务、签名。这些是 APK/AAB 里的事实,也是本文要讲的。
网上大多数“上架被拒”的文章只讲第一类,第二类往往一笔带过。
第 1 步:上传前先体检
我用 aapt2、bundletool 1.18.3 和 NDK r27 构建了 before.aab,再按 AAB 的签名方式用 jarsigner 签名。它集中了实际项目里最常见的几个问题:
- 包名
com.example.rejectdemo,targetSdkVersion35 - application 上写了
android:debuggable="true" - 原生库只有
lib/armeabi-v7a/ - 申请了
READ_SMS、QUERY_ALL_PACKAGES、READ_MEDIA_IMAGES和FOREGROUND_SERVICE - 一个
foregroundServiceType="dataSync"的服务,却没有FOREGROUND_SERVICE_DATA_SYNC - 签名证书主题是
CN=Android Debug,O=Android,C=US,也就是 Android SDK 调试密钥的主题
打开 合规检查页(默认已选 Google Play 场景),把文件拖进去。分析核心报出的未通过项如下(输出保持英文原文):
PLAY-01 high targetSdkVersion=35
required: >= 36 (phone/tablet, since 2026-08-31)
note: An extension to 2026-11-01 can be requested in Play Console;
without one, submissions are rejected.
PLAY-04 reject lib/armeabi-v7a/ present, lib/arm64-v8a/ missing
PLAY-06 reject application android:debuggable="true"
PLAY-07 reject package="com.example.rejectdemo" (reserved prefix com.example.*)
PLAY-08 reject uses-permission android.permission.READ_SMS
PLAY-09 high uses-permission android.permission.QUERY_ALL_PACKAGES
<queries>: none
PLAY-16 reject uses-permission android.permission.READ_MEDIA_IMAGES
targetSdkVersion=35
SIGN-01 reject v1 signer: CN=Android Debug,O=Android,C=US — Android SDK debug certificate
(SHA-256 575e39d821c4349d0e7699bdac8a01192e93d38d11506cc9aa34948c0fbf83d4)
PLAY-15 reject service com.example.rejectdemo.SyncService (dataSync): missing FOREGROUND_SERVICE_DATA_SYNC
targetSdkVersion=35
中文界面里,这几级分别显示为拒审、高风险和建议。我用 SDK 自带工具做了交叉核对:bundletool dump manifest 列出的是同样四个权限,keytool -printcert -jarfile before.aab 打印的 SHA-256 一致,llvm-readelf -lW 显示 r27 编译的 arm64 库 LOAD 段按 0x1000 对齐。
把检查页里的日期改成 2026-11-01 再查一次,PLAY-01 就变成了拒审,因为延期窗口已经关了。
第 2 步:逐项修掉包里的问题
下面每一节先给出规则编号、级别和触发条件(与工具代码里的定义一致),再说在自己这边怎么改。
目标 API 级别(PLAY-01)
拒审。手机和平板从 2026-08-31 起要求 targetSdk 至少 36;在还能申请延期到 2026-11-01 的这段时间里,35 的包报为高风险。Wear OS 和 Automotive 要求 35,TV 和 XR 要求 34,工具根据 required 的 <uses-feature> 判断设备形态。
Google 的 目标 API 级别要求 原文是:“New apps and app updates must target Android 16 (API level 36) or higher to be submitted to Google Play”,并可申请延期到 2026 年 11 月 1 日。改 targetSdk 只是一行,真正花时间的是按新版本的行为变更做回归测试。
64 位与 16 KB 原生库(PLAY-04、PLAY-02、PLAY-03)
PLAY-04,拒审:有 armeabi-v7a/armeabi 却没有 arm64-v8a,或有 x86 却没有 x86_64,或者 ELF 的位数/机器类型与所在目录不符。Google 的 64 位要求 写得很直接:每支持一个 32 位架构,就必须同时提供对应的 64 位架构。在 abiFilters 里加上 64 位 ABI,并为每个预编译 .so 拿到 64 位版本。
PLAY-02(64 位库里任一 PT_LOAD 的 p_align < 0x4000)和 PLAY-03(仅 APK:不压缩的 64 位 .so 在 ZIP 里的偏移不是 16384 的倍数)在 2027-02-01 之前为高风险,之后为拒审。Google 的 16 KB 页面大小 说明,从 2027 年 2 月 1 日起,不支持 16 KB 页面的更新将无法发布。NDK r28 起默认按 16 KB 对齐。完整步骤见 检查 APK 是否满足 16 KB 页面大小要求。
发布构建、包名与签名(PLAY-06、PLAY-07、SIGN-01、SIGN-02)
- PLAY-06,拒审:
<application>上有android:debuggable="true"或android:testOnly="true"。用release构建类型打包,删掉手动加的这两个属性。 - PLAY-07,拒审:包名在
com.example、com.android或android之下。Google 的 应用 ID 说明 里com.example.myapp只是占位示例,并没有列出保留前缀;不过 Play Console 拒收com.example包名是开发者普遍遇到的情况,按硬性问题处理即可。现在就换成自己域名反写的 ID,同一页面也提醒:ID 一改,Play 会当成另一个应用。 - SIGN-01,Play 场景拒审:任一签名证书的 CN 为
Android Debug。Google 的 Sign your app 写明,包括 Google Play 在内的大多数应用商店都不接受调试证书签名的应用。 - SIGN-02,Play 场景拒审:签名证书的 SHA-256 与私钥已公开的密钥一致,比如 AOSP 测试密钥,或 React Native 模板自带的
debug.keystore。React Native 模板的 release 构建类型在你配置自己的密钥之前,用的就是它。
生成有效期至少 25 年的上传密钥(同一页面说明 Play 要求密钥有效期晚于 2033 年 10 月 22 日),并启用 Play App Signing。SIGN-04(RSA 小于 2048 位、DSA 或 MD5:高风险)和 SIGN-05(证书在 2033-10-22 前到期:APK 为拒审;AAB 只是建议,因为 AAB 上的是上传密钥)管剩下的部分。
用 App Bundle,而不是 APK(PLAY-05)
把同一构建的通用 APK 拖进检查页,会多出一条 PLAY-05,高风险:“input is an APK”,备注是新应用必须以 AAB 上传。只有 Android TV 应用是拒审,因为 Play 已不再接受 TV 应用用 APK 更新。用 ./gradlew bundleRelease 生成 AAB。
需要声明的权限
这一类结果并不等于“权限被禁止”,而是说这个权限只允许特定的核心用途,Play Console 会要求你填声明。各项检查和推荐的替代方案:
| 规则 | 级别 | 触发条件 | 更窄的替代方案 |
|---|---|---|---|
| PLAY-08 | 拒审 | 任一短信或通话记录权限 | SMS Retriever API |
| PLAY-09 | 高风险 | QUERY_ALL_PACKAGES |
用 <queries> 只声明需要的包 |
| PLAY-10 | 拒审 | MANAGE_EXTERNAL_STORAGE(只有 requestLegacyExternalStorage="true" 时为高风险) |
SAF、MediaStore |
| PLAY-12 | 高风险 | REQUEST_INSTALL_PACKAGES |
自更新改用 Play In-App Updates |
| PLAY-13 | 高风险 | ACCESS_BACKGROUND_LOCATION |
只用前台位置 |
| PLAY-14 | 高风险 | USE_EXACT_ALARM(只有 SCHEDULE_EXACT_ALARM 时为建议) |
非精确闹钟、WorkManager |
| PLAY-16 | 拒审 | READ_MEDIA_IMAGES 或 READ_MEDIA_VIDEO,仅当 targetSdk ≥ 33 |
系统 Photo Picker |
| PLAY-17 | 建议(targetSdk ≥ 37 时为高风险) | READ_CONTACTS |
Android Contact Picker |
| PLAY-11 | 高风险 | 受 BIND_ACCESSIBILITY_SERVICE 保护的服务 |
填声明并做应用内显著披露 |
政策页面对“谁能保留”写得很具体。短信和通话记录:应用必须已被设为默认短信、电话或助理处理程序(SMS and Call Log permissions)。照片和视频:只有核心功能依赖广泛访问照片视频的应用才符合(Photo and video permissions)。QUERY_ALL_PACKAGES:设备搜索、杀毒、文件管理、浏览器这类应用(QUERY_ALL_PACKAGES)。不符合的要删掉权限,符合的要提交声明。
政策截止日期表 里还有两个日期值得记下:2027-01-27 起,“通过电话验证账户”不再是 READ_CALL_LOG 的允许用途;没有广泛读取通讯录需求的应用,要改用 Contact Picker。
前台服务(PLAY-15)
仅当 targetSdk ≥ 34。所有场景拒审:服务声明了 foregroundServiceType,却缺少对应的 FOREGROUND_SERVICE_<TYPE> 权限,一启动就会崩溃。高风险:有 FOREGROUND_SERVICE 却没有任何声明了类型的服务;Play 场景下另含每个带类型的前台服务(需要在 Play Console 声明)以及 USE_FULL_SCREEN_INTENT。
Google 的 前台服务要求 要求在“应用内容”页面为每种类型填写声明:功能说明、任务被推迟或中断对用户的影响,以及一段演示用户如何触发该功能的视频。全屏通知只对通话和闹钟类应用默认开放。
第 3 步:找出权限是谁带进来的
很多时候 READ_SMS 根本不是你写的,是某个库合并进来的。Android Studio 打开 AndroidManifest.xml,底部的 Merged Manifest 标签会标出每个元素的来源;构建时完整日志写在 build/outputs/logs/manifest-merger-<buildVariant>-report.txt(见 Merge multiple manifest files)。如果需要这个权限的库功能你并没有用到,在自己的 Manifest 里移除它:
<uses-permission android:name="android.permission.READ_SMS" tools:node="remove" />
库如果真的用到了这个权限,删掉后对应功能可能失效,所以要么测一遍这个功能,要么换掉这个库。
第 4 步:修完再跑一遍
after.aab 改用包名 dev.example.rejectdemo、targetSdk 36、正式上传密钥(CN=Example Upload),原生库用 NDK r28 同时编译 armeabi-v7a 和 arm64-v8a,去掉了 debuggable、短信、包查询和媒体权限,并补上 FOREGROUND_SERVICE_DATA_SYNC。只剩一条:
PLAY-15 high service dev.example.rejectdemo.SyncService: foregroundServiceType=dataSync (Play Console declaration)
targetSdkVersion=36
这正是预期结果:包本身已经没问题,剩下的是要在 Play Console 里填的前台服务声明。
文件里看不到的部分
报告干净只代表发布包没问题,下面这些仍要自己逐项确认:
- 数据安全表单与应用里每个 SDK 的数据收集一致
- 隐私政策链接;用户能注册账号的话,还要提供删除账号的入口
- 应用有登录墙的话,给审核人员提供登录信息
- 商店信息不误导、不冒充他人
- 2023 年 11 月 13 日之后注册的个人开发者账号:上线正式版前,需要至少 12 名测试者连续参与 14 天的封闭测试(测试要求)
Play Console 自己也会在“发布概览”页做 预审检查,覆盖缺失的声明、崩溃率和可能的政策问题;Google 同样说明,通过预审不代表一定能过审。本工具的结果不代表 Google 的审核结论。
被拒之后:看状态、重新提交、申诉
- 先看原因。在 Play Console 选中应用,左侧菜单打开 Policy status(查看应用的政策状态)。被拒时,上一个成功发布的版本仍在 Google Play 上,邮件里会写明违反的是哪条政策。
- 修好后重新提交。Google 的 重新提交步骤 要求除了正式版,还要更新所有发布轨道(内部、封闭、开放测试),并停用不合规的 bundle;原文警告,不停用的话重新提交会失败。
- 只有确信判错时才申诉。同一页面写明,每次下架、暂停或其他处置只能申诉一次。如果确实是包里的问题触发了规则,就修包,申诉改变不了 Manifest 里写的内容。
- 预留审核时间。Google 说部分账号的审核“在特殊情况下可能需要七天或更久”(Publish your app)。
关于加壳和加固
如果应用用了商业防护运行库,检查结果会给出 PROT-01,级别为建议。加壳和加固是正当的保护手段,这条结果只说明利弊:壳的启动代码在启发式扫描引擎看来可能像恶意软件的打包器;带签名自校验的防护在 Play App Signing 下可能失效,因为 Google 会重新签名。选择明确支持 Play App Signing 的方案,长期使用同一签名证书,不要从 Google Play 以外下载可执行代码。
REA 在这里能做什么
REA(morluto 的开源项目)6.4.0 没有任何 Google Play 政策检查。它的 inspect-android-package 通过你自行提供的 JADX 引擎读取 APK 的包名、SDK 级别和权限,契约里写明它“does not execute the APK or verify signatures”(不运行 APK,也不校验签名)。它只接受 APK,AAB 要先用 bundletool 转换(方法)。它的 JADX 类搜索可以帮你找到使用某个权限的代码,填声明时正好要解释这部分。配置方法见 JADX MCP server 接入 Claude Code。
常见问题
报告全部通过,Play 就一定能过审吗? 不一定。这只说明没有触发已知的文件级规则。声明、数据安全表单、商店信息和运行时行为是另外审核的。
权限已经删了,为什么还因为它被拒? 通常是某个测试轨道上还有仍申请该权限的旧 bundle 处于启用状态。按 Google 的重新提交步骤,在每个轨道上都停用它。
受限权限能保留吗? 可以,前提是你的核心用途符合政策。在 Play Console 提交声明,并准备好演示视频。工具仍然会报出这条,因为审核这一关还在后面。
下一步
- 检查自己的构建:Google Play 上架合规检查,或完整的 APK/AAB 分析与上架体检
- 逐步解决 PLAY-02 和 PLAY-03:16 KB 页面大小检查
- 签名密钥与指纹:安装包与现有包冲突、Android 开发者验证
- 从权限追到代码:用 REA 分析 Android APK
- 参考资料:Google Play 政策截止日期、REA 的 DISCLAIMER.md