这是《Google Play Android应用打包指南》的续篇。如果你还没看过基础的打包和上架流程,建议先阅读:Google Play Android应用打包指南
相信很多Android开发者都有过这样的经历:应用打包完成,信心满满地提交到Google Play,结果没过多久就收到了审核被拒的邮件😅。别问我怎么知道的,问就是经验丰富…
下面整理了几个Google Play审核中最容易踩坑的几个问题,适配设备为0,审核被拒的"元凶"。
一、MANAGE_EXTERNAL_STORAGE权限
什么情况下会遇到这个权限?
MANAGE_EXTERNAL_STORAGE
这个权限,说白了就是"我要访问你设备上的所有文件"。Google从Android 11开始引入这个权限,目的就是为了保护用户隐私。
哪些场景真的需要这个权限?
先说重点:90%的应用其实都不需要这个权限!
真正需要的场景,Google官方认可的就这几种:
文件管理器应用
- 你的应用就是专门用来管理文件的,比如ES文件浏览器
- 需要访问、删除、移动设备上任意位置的文件
系统级备份应用
- 要备份用户的所有数据,包括其他应用的数据
- 典型例子:钛备份
专业文档处理应用
- 需要处理设备上任意位置的文档文件
- 比如专业的PDF编辑器
媒体编辑应用
- 需要访问设备上任意位置的视频、音频文件进行编辑
- 典型例子:专业视频剪辑软件
Google审核的"雷区"在哪里?
这些情况铁定被拒:
- 社交应用申请这个权限 - 你一个聊天软件要访问所有文件干嘛?
- 游戏应用申请这个权限 - 游戏只要能读写自己的存档就够了
- 工具类应用滥用 - 比如手电筒应用申请这个权限
- 没有合理的解释说明 - 申请了权限但说不清楚为什么要用
怎么向Google证明你真的需要?
如果你确实需要这个权限,那就得做好以下几点:
- 详细的使用说明:在上传应用时,详细解释为什么需要这个权限
- 功能演示视频:录制视频展示你的应用如何使用这个权限
- 隐私政策更新:在隐私政策中明确说明会访问哪些文件,用途是什么
- 用户引导界面:在应用中添加清晰的权限申请引导页面
替代方案:大部分情况用这个就够了
说实话,大部分应用用Storage Access Framework (SAF)
就完全够用了:
// 使用SAF让用户选择文件,既安全又符合Google要求
private fun openFilePicker() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*" // 可以根据需要指定文件类型
}
startActivityForResult(intent, REQUEST_CODE_PICK_FILE)
}
这样既能访问用户选择的文件,又不会被Google质疑你的动机。
我的实战经验分享
经过仔细分析和验证,我采取了以下解决方案:
-
权限需求分析
- 详细梳理了应用的所有功能模块
- 确认没有需要访问设备所有文件的场景
- 现有文件操作都限于应用私有目录和共享目录
-
解决方案
- 直接从
AndroidManifest.xml
中移除了MANAGE_EXTERNAL_STORAGE
权限 - 使用
READ_EXTERNAL_STORAGE
和WRITE_EXTERNAL_STORAGE
替代 - 对于Android 11及以上版本,采用
MediaStore
API访问媒体文件
- 直接从
-
验证测试
- 确认所有文件相关功能均正常运行
- 用户体验没有受到任何影响
经验总结:不要盲目申请高危权限,应该根据实际需求选择最小权限集。很多时候我们以为需要的权限,通过合理的技术方案是完全可以避免的。
二、uses-feature标签
从上图可以看到,设备不受支持的原因是因为应用声明了android.hardware.camera
和android.hardware.camera.autofocus
这两个硬件特性要求。
相机相关的功能声明问题
找到对应的AndroidManifest.xml
里确实有这样的配置:
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
这些功能声明该不该删?
这是个很实际的问题。你得根据自己应用的实际情况来判断:
可以放心删除的情况:
- 纯展示类应用:比如新闻阅读、天气预报
- 不涉及扫码的工具应用:计算器、日历等
- SDK集成但不使用相机功能:比如你集成了某个SDK,但实际不用它的相机功能
不能删除的情况:
- 有拍照功能:用户头像上传、照片分享等
- 有扫码功能:二维码扫描、条码识别
- 支付相关:支付宝、微信支付的扫码功能
- 社交功能:扫码添加好友
推荐的安全配置方法
如果你确实需要相机功能,建议这样配置:
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.autofocus"
android:required="false" />
为什么要设置android:required="false"
?
- 让没有相机的设备(比如某些平板)也能安装你的应用
- 在代码里动态检查相机是否可用,提供更好的用户体验
- 避免Google Play把你的应用从某些设备类型中过滤掉
检查方法:怎么确定该不该删?
- 依赖库检查:
- 看看你用的第三方库是否真的需要相机
- 检查集成的SDK文档说明
- 功能测试:
- 删除这些声明后,在模拟器上测试应用功能
- 看看是否有功能受到影响
实战经验分享
由于集成了一些第三方SDK(如支付、社交分享等),这些SDK确实需要相机相关功能。经过权衡,采用了设置android:required="false"的方式。
三、WebView SSL错误处理
我们发现您的应用存在安全漏洞,可能会泄露用户信息或损害用户设备。这种情况违反了"设备和网络滥用"政策。具体而言,您的应用包含不安全的WebViewSSL错误处理程序实现方式。
什么是WebView SSL错误处理问题?
如果你的应用中使用了WebView,那么你很可能会遇到这个问题。Google Play会检测你的应用是否存在"不安全的WebViewSSL错误处理程序实现方式"。
具体来说,就是在WebView遇到SSL证书错误时,你的代码是怎么处理的。如果项目代码为了"简单",会选择忽略所有SSL错误,这就踩坑了。
危险的代码示例
这样的代码会被Google拒绝:
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
handler.proceed(); // 这行代码很危险!
}
这种实现方式的问题在于:
- 无条件信任所有证书:即使是伪造的、过期的、不匹配的证书
- 中间人攻击风险:攻击者可以轻易拦截和修改网络通信
- 用户数据泄露:敏感信息可能被第三方获取
如何排查项目中的SSL处理问题
-
搜索关键代码:
# 搜索SSL错误处理相关代码 grep -r "onReceivedSslError" src/ grep -r "handler.proceed" src/ grep -r "SslErrorHandler" src/
-
检查WebViewClient实现:
- 查找所有继承
WebViewClient
的类 - 重点检查
onReceivedSslError
方法的实现
- 查找所有继承
-
检查第三方库:
- 有些SDK可能也包含WebView实现
- 确保所有WebView相关代码都是安全的
安全的修复方案
推荐的安全实现:
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
// 安全处理方式:默认取消加载,保证安全性
handler.cancel();
// 可选:向用户显示错误信息
Toast.makeText(mContext, "SSL证书错误,网页可能不安全", Toast.LENGTH_SHORT).show();
}
如果你确实需要支持特定域名的自签名证书(比如企业内部系统),可以这样处理:
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
String url = error.getUrl();
// 只对可信的域名放行,其他一律拒绝
if (url.contains("yourtrustedomain.com") || url.contains("intranet.company.com")) {
handler.proceed();
} else {
handler.cancel();
// 提示用户证书错误
Toast.makeText(mContext, "SSL证书错误,网页可能不安全", Toast.LENGTH_SHORT).show();
}
}
最佳实践建议
-
默认拒绝策略:除非有明确的业务需求,否则始终使用
handler.cancel()
-
白名单机制:如果必须支持自签名证书,建议使用域名白名单
-
用户提示:当遇到SSL错误时,向用户说明安全风险
-
测试验证:修复后在测试环境验证功能是否正常
-
文档记录:在代码中添加详细注释,说明为什么这样处理
相关问题排查清单
修复SSL处理问题时,建议检查这些项目:
- 搜索并修复所有
handler.proceed()
调用 - 确保所有WebViewClient实现都使用安全的SSL处理
- 检查第三方SDK是否存在类似问题
- 测试修复后的功能是否正常工作
- 在不同网络环境下测试SSL证书验证
四、第三方SDK违规问题:Tencent TBS SDK案例
问题描述
在Google Play审核过程中,可能会遇到关于第三方SDK违反"设备和网络滥用"政策的问题。典型的错误提示如下:
问题分析
这个问题主要出现在使用了Tencent TBS(腾讯浏览服务)SDK的应用中。TBS SDK是腾讯提供的一个WebView增强组件,但某些版本可能会从Google Play以外的来源下载可执行代码,这违反了Google Play的安全政策。
具体违规行为:
- SDK会动态下载dex、JAR或so文件
- 这些文件来源不在Google Play管控范围内
- 可能存在安全风险,违反了Google的安全政策
解决方案
升级SDK版本
将Tencent TBS SDK升级到符合Google Play政策的版本:
// 问题版本
implementation 'com.tencent.tbs.tbssdk:sdk:sdk:43993'
// 修复后版本
implementation 'com.tencent.tbs:tbssdk:44286'
五、实战经验:如何避开Google的审核"雷区"
常见的审核被拒原因
根据我的经验,这些是最容易被拒的原因:
- 权限申请不合理:申请了用不到的高危权限
- 功能声明冗余:声明了一堆用不到的硬件功能
- WebView SSL处理不安全:忽略SSL证书错误,存在安全漏洞
- 第三方SDK违规:使用了不符合Google Play政策
- 缺少使用说明:没有向用户解释为什么需要某些
- 测试不充分:在某些设备上应用崩溃或功能异常
提交前的自检清单
在提交应用到Google Play之前,建议按这个清单检查一遍:
- 只申请了真正需要的权限
- 每个权限都有合理的使用说明
- 功能声明与实际功能一致
- WebView SSL错误处理使用安全的实现方式
- 第三方SDK版本符合Google Play
- 版本号格式符合Google Play规范
- 在不同设备类型上测试过应用
- 隐私政策包含了所有敏感权限的说明
- 应用内有清晰的权限申请引导
被拒后的处理策略
如果不幸被拒了,别慌,按这个流程来:
- 仔细阅读拒绝邮件:Google会告诉你具体问题在哪里
- 修复问题:根据反馈调整权限配置或功能声明
- 补充说明:在重新提交时,详细说明你做了哪些修改
- 准备申诉材料:如果认为审核有误,准备详细的申诉说明
六、总结:安全开发的"黄金法则"
总结几条"黄金法则":
最小权限原则:只申请真正需要的权限,一个都不多要
功能匹配原则:功能声明要与实际功能严格匹配
安全第一原则:WebView等网络组件必须使用安全的实现方式
用户友好原则:向用户清楚解释为什么需要某些权限
兼容性原则:考虑不同设备类型的兼容性
测试充分原则:在多种设备和场景下充分测试
SDK合规原则:确保所有第三方SDK符合Google Play政策要求