安卓10及以上版本WhatsApp状态保存器权限失效,卡在启动页
解决安卓10+版本WhatsApp状态保存器权限失效卡启动页问题
核心问题分析
你的启动页代码存在几个关键逻辑错误,导致安卓10及以上版本权限验证失败:
- 安卓11(API30+)权限请求与检查不匹配:你用
ACTION_OPEN_DOCUMENT_TREE请求SAF目录访问,但权限检查却在验证MANAGE_EXTERNAL_STORAGE全存储权限,两者完全无关。用户即使选择了目录,权限检查依然会返回未授予,导致卡在启动页。 - 安卓10(API29)适配缺失:安卓10引入分区存储后,
WRITE_EXTERNAL_STORAGE不再提供全局写入权限,旧的权限请求逻辑完全失效。 - 未处理SAF请求回调:调用
startActivityForResult打开SAF后,没有重写onActivityResult处理用户选择结果,权限状态无法更新。 - 权限逻辑冗余:安卓11+访问WhatsApp状态目录(
Android/media/com.whatsapp/WhatsApp/Media/.Statuses)不需要额外权限,你的代码却错误地请求了全存储权限。
修复方案(符合谷歌政策的适配)
WhatsApp状态文件存储在公共媒体目录,安卓10+可以通过分区存储规则直接访问,无需申请全存储权限。以下是修正后的完整代码:
public class Splash extends AppCompatActivity { private final Handler handler = new Handler(); private static final int REQUEST_STORAGE_PERMISSION = 1234; private static final int REQUEST_SAF_DIRECTORY = 5678; // WhatsApp状态文件所在目录 private static final String WHATSAPP_STATUSES_PATH = "Android/media/com.whatsapp/WhatsApp/Media/.Statuses"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.splash_activity); getSupportActionBar().hide(); if (canAccessStatusFiles()) { next(); return; } requestNecessaryPermissions(); } @Override protected void onResume() { super.onResume(); if (canAccessStatusFiles()) { next(); } } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_SAF_DIRECTORY && resultCode == RESULT_OK && data != null) { // 持久化SAF目录访问权限 Uri treeUri = data.getData(); getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); if (canAccessStatusFiles()) { next(); } else { showPermissionDeniedDialog(); } } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_STORAGE_PERMISSION) { boolean allGranted = true; for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { allGranted = false; break; } } if (allGranted) { next(); } else { showPermissionDeniedDialog(); } } } // 检查是否能访问WhatsApp状态文件 private boolean canAccessStatusFiles() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // 安卓11+:直接访问公共媒体目录,无需权限 File statusDir = new File(getExternalFilesDir(null).getParentFile(), WHATSAPP_STATUSES_PATH); return statusDir.exists() && statusDir.canRead(); } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) { // 安卓10:先检查READ权限,无权限则检查是否已有SAF授权 boolean hasReadPerm = ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; if (!hasReadPerm) { // 验证已持久化的SAF权限 for (UriPermission perm : getContentResolver().getPersistedUriPermissions()) { if (perm.isReadPermission()) { DocumentFile tree = DocumentFile.fromTreeUri(this, perm.getUri()); if (tree != null && ".Statuses".equals(tree.getName())) { return true; } } } return false; } return true; } else { // 安卓9及以下:检查传统读写权限 return ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; } } // 根据系统版本请求对应权限 private void requestNecessaryPermissions() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { File statusDir = new File(getExternalFilesDir(null).getParentFile(), WHATSAPP_STATUSES_PATH); if (!statusDir.exists() || !statusDir.canRead()) { showSAFDialog(); } } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_STORAGE_PERMISSION); } else { showSAFDialog(); } } else { requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_STORAGE_PERMISSION); } } // 引导用户通过SAF选择目录 private void showSAFDialog() { new AlertDialog.Builder(this) .setTitle("需要存储访问权限") .setMessage("请选择WhatsApp的状态目录以访问文件") .setPositiveButton("前往选择", (dialog, which) -> { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); // 直接引导到Android/media目录,减少用户操作 Uri initialUri = Uri.parse("content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fmedia"); intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialUri); startActivityForResult(intent, REQUEST_SAF_DIRECTORY); }) .setNegativeButton("取消", (dialog, which) -> finish()) .show(); } // 权限拒绝提示 private void showPermissionDeniedDialog() { new AlertDialog.Builder(this) .setTitle("权限被拒绝") .setMessage("应用需要存储权限才能正常工作,请在设置中开启") .setPositiveButton("前往设置", (dialog, which) -> { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivity(intent); finish(); }) .setNegativeButton("取消", (dialog, which) -> finish()) .show(); } private void next() { handler.postDelayed(() -> { startActivity(new Intent(Splash.this, MainActivity.class)); finish(); }, 1000); } }
关键优化点
- 安卓11+适配:遵循谷歌分区存储政策,直接访问
Android/media下的公共目录,无需额外权限。 - 安卓10适配:先尝试请求
READ_EXTERNAL_STORAGE,失败则引导用户通过SAF选择目录并持久化权限。 - 移除冗余权限:删除了不必要的
MANAGE_EXTERNAL_STORAGE请求,避免谷歌审核风险。 - 完善错误处理:添加了权限拒绝后的引导逻辑,提升用户体验。
内容的提问来源于stack exchange,提问作者Suraj
相关产品推荐
相关产品推荐

