You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

安卓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);
    }
}

关键优化点

  1. 安卓11+适配:遵循谷歌分区存储政策,直接访问Android/media下的公共目录,无需额外权限。
  2. 安卓10适配:先尝试请求READ_EXTERNAL_STORAGE,失败则引导用户通过SAF选择目录并持久化权限。
  3. 移除冗余权限:删除了不必要的MANAGE_EXTERNAL_STORAGE请求,避免谷歌审核风险。
  4. 完善错误处理:添加了权限拒绝后的引导逻辑,提升用户体验。

内容的提问来源于stack exchange,提问作者Suraj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 12:54:26