Google Play 拒审原因:上传前就能在 APK/AAB 里查出来的那些
本页目录
Google Play 拒审,很多时候跟截图、描述没关系,问题就出在发布包本身:targetSdkVersion 低于当前要求、只有 32 位原生库、打的是可调试包、用调试证书签名,或者申请了 READ_SMS 这类受限权限却没填声明。这些全都能在上传前从 APK/AAB 里读出来,也都能在自己的构建里改掉。
本文先构建一个带着常见问题的 App Bundle,交给本站的 Google Play 上架合规检查 跑一遍,逐条修完再跑一遍。下面的输出都来自 2026-10-14 的实际运行。检查在浏览器里完成,文件不会上传。
拒审分两类
- 商店信息和各类声明:隐私政策、数据安全表单、内容分级、广告声明、给审核人员的登录信息。这些从文件里看不到,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 步:逐项修掉包里的问题
| 结果 | 怎么改 |
|---|---|
| PLAY-01 目标 API | 手机和平板把 targetSdk 提到 36(Wear OS 和 Automotive 要 35,TV 和 XR 要 34;检查页根据 required 的 <uses-feature> 判断设备形态)。改这一行很快,真正花时间的是按新版本的行为变更做回归测试。见 目标 API 级别要求。 |
| PLAY-04 只有 32 位 | 在 abiFilters 里加上 arm64-v8a(有 x86 的话再加 x86_64),并为每个预编译 .so 拿到 64 位版本(64 位要求)。 |
| PLAY-02 / PLAY-03 16 KB | 2027-02-01 之前为高风险,之后为拒审(16 KB 页面大小)。NDK r28 起默认按 16 KB 对齐,完整步骤见 检查 APK 是否满足 16 KB 页面大小要求。 |
| PLAY-06 可调试 / testOnly | 用 release 构建类型打包,删掉手动加的 android:debuggable 或 android:testOnly。 |
PLAY-07 com.example 包名 |
换成自己域名反写的应用 ID。 |
| SIGN-01 / SIGN-02 调试或公开密钥 | 用自己的上传密钥签名,并启用 Play App Signing。 |
| PLAY-05 传的是 APK | 用 ./gradlew bundleRelease 生成 AAB。新应用必须上传 AAB;只有 Android TV 应用的 APK 更新会被直接拒。 |
表格背后有几个坑:
com.example在文档里并没有写成保留前缀。Google 的 应用 ID 说明 只把com.example.myapp当占位示例,但 Play Console 拒收com.example包名是开发者普遍遇到的情况。第一次上传前就改掉:同一页面提醒,ID 一改,Play 会当成另一个应用。- React Native 模板自带公开密钥。在你配置自己的密钥之前,模板的 release 构建类型用的就是自带的
debug.keystore,SIGN-02 会像识别 AOSP 测试密钥一样识别它。 - 密钥有效期。Play 要求上传密钥的有效期晚于 2033 年 10 月 22 日(Sign your app),所以生成时直接给 25 年以上。SIGN-05 会标出更早到期的证书:APK 为拒审;AAB 只是建议,因为 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 保护的服务 |
填声明并做应用内显著披露 |
谁能保留:短信和通话记录只给默认短信、电话或助理处理程序(政策);广泛访问照片视频只给以此为核心功能的应用(政策);QUERY_ALL_PACKAGES 给设备搜索、杀毒、文件管理、浏览器这类应用(政策)。不符合的删掉权限;符合的提交声明,检查页仍会报出这条,因为审核这一关还在后面。政策截止日期表 里还有两个日期:2027-01-27 起,“通过电话验证账户”不再是 READ_CALL_LOG 的允许用途;没有广泛读取通讯录需求的应用,要改用 Contact Picker。
前台服务(PLAY-15)
targetSdk ≥ 34 时,服务声明了 foregroundServiceType 却缺少对应的 FOREGROUND_SERVICE_<TYPE> 权限,一启动就会崩溃,所以检查页在所有场景下都判拒审。在 Play 上,每个带类型的前台服务还要在“应用内容”页面填写声明:功能说明、对用户的影响,以及一段演示用户如何触发该功能的视频(前台服务要求)。USE_FULL_SCREEN_INTENT 只对通话和闹钟类应用默认开放。
第 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 同样说明,通过预审不代表一定能过审,本站检查页的结果也一样。
被拒之后:看状态、重新提交、申诉
- 先看原因。在 Play Console 选中应用,左侧菜单打开 Policy status(查看应用的政策状态)。被拒时,上一个成功发布的版本仍在 Google Play 上,邮件里会写明违反的是哪条政策。
- 修好后重新提交。Google 的 重新提交步骤 要求除了正式版,还要更新所有发布轨道(内部、封闭、开放测试),并停用不合规的 bundle;原文警告,不停用的话重新提交会失败。权限明明删了还因为它被拒,通常就是某个测试轨道上还有申请该权限的旧 bundle 处于启用状态。
- 只有确信判错时才申诉。同一页面写明,每次下架、暂停或其他处置只能申诉一次。如果确实是包里的问题触发了规则,就修包,申诉改变不了 Manifest 里写的内容。
- 预留审核时间。Google 说部分账号的审核“在特殊情况下可能需要七天或更久”(Publish your app)。
关于加壳和加固
如果应用用了商业防护运行库,检查结果会给出 PROT-01,级别为建议。加壳和加固是正当的保护手段,这条结果只说明利弊:壳的启动代码在启发式扫描引擎看来可能像恶意软件的打包器;带签名自校验的防护在 Play App Signing 下可能失效,因为 Google 会重新签名。选择明确支持 Play App Signing 的方案,长期使用同一签名证书,不要从 Google Play 以外下载可执行代码。