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

Android 14读取OBB扩展文件音频遇权限拒绝问题求助

Android 14访问OBB文件权限被拒问题解决

问题根源

Android 14收紧了对OBB扩展文件的访问限制,禁止直接通过Environment.getExternalStorageDirectory()拼接的路径访问OBB目录,必须使用系统提供的StorageManagerAPI来挂载并获取合法的访问路径。

解决方案

1. 配置FileProvider(解决Uri暴露问题)

在AndroidManifest.xml中注册FileProvider,用于生成安全的文件Uri:

<application>
    ...
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
    ...
</application>

在res/xml目录下创建file_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <cache-path name="cache_files" path="/" />
</paths>

2. 修改代码适配Android 14权限规则

替换原有的直接路径访问逻辑,改用StorageManager挂载OBB文件并获取合法路径:

private void playSelectedSong(String songName) {
    StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
    String obbFileName = "main.1.com.mesbahi.free.player.obb";
    // 获取应用专属OBB目录路径,替代硬编码外部存储路径
    String obbFilePath = new File(getObbDir(), obbFileName).getAbsolutePath();

    ObbInfo obbInfo = storageManager.getObbInfo(obbFilePath);
    if (obbInfo == null) {
        sendFileNotFoundBroadcast();
        return;
    }

    if (!obbInfo.mounted) {
        // 异步挂载OBB文件
        boolean mountSuccess = storageManager.mountObb(obbFilePath, null, new OnObbStateChangeListener() {
            @Override
            public void onObbStateChange(String path, int state) {
                switch (state) {
                    case OnObbStateChangeListener.MOUNTED:
                        processObbFile(storageManager.getMountedObbPath(path), songName);
                        break;
                    case OnObbStateChangeListener.MOUNT_FAILED:
                        sendFileNotFoundBroadcast();
                        break;
                }
            }
        });
        if (!mountSuccess) {
            sendFileNotFoundBroadcast();
        }
    } else {
        // OBB已挂载,直接处理
        processObbFile(storageManager.getMountedObbPath(obbFilePath), songName);
    }

    sendUpdateButtonStateBroadcast(true);
}

private void processObbFile(String mountedObbPath, String songName) {
    try {
        ZipFile obbZipFile = new ZipFile(mountedObbPath);
        Enumeration<? extends ZipEntry> entries = obbZipFile.entries();

        boolean fileFound = false;
        while (entries.hasMoreElements()) {
            ZipEntry entry = entries.nextElement();
            if (entry.getName().startsWith(songName)) {
                InputStream inputStream = obbZipFile.getInputStream(entry);
                File tempFile = File.createTempFile(songName, "." + getFileExtension(entry), getCacheDir());
                FileOutputStream outputStream = new FileOutputStream(tempFile);
                byte[] buffer = new byte[1024];
                int length;
                while ((length = inputStream.read(buffer)) > 0) {
                    outputStream.write(buffer, 0, length);
                }
                outputStream.close();
                inputStream.close();

                // 使用FileProvider生成安全Uri,避免Android 7+的FileUriExposedException
                Uri uri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", tempFile);
                // 授予媒体播放器读取权限
                grantUriPermission("com.android.mediaplayer", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);

                MediaItem mediaItem = MediaItem.fromUri(uri);
                player.setMediaItem(mediaItem);
                player.prepare();
                player.play();
                player.setRepeatMode(Player.REPEAT_MODE_ONE);
                fileFound = true;
                break;
            }
        }

        if (!fileFound) {
            sendFileNotFoundBroadcast();
        }
    } catch (IOException e) {
        e.printStackTrace();
        sendFileNotFoundBroadcast();
    }
}

private void sendFileNotFoundBroadcast() {
    Intent fileNotFoundIntent = new Intent("com.mesbahi.free.player.FILE_NOT_FOUND");
    LocalBroadcastManager.getInstance(this).sendBroadcast(fileNotFoundIntent);
}

private void sendUpdateButtonStateBroadcast(boolean isPlaying) {
    Intent updateButtonIntent = new Intent("com.mesbahi.free.player.UPDATE_BUTTON_STATE");
    updateButtonIntent.putExtra("isPlaying", isPlaying);
    LocalBroadcastManager.getInstance(this).sendBroadcast(updateButtonIntent);
}

private String getFileExtension(ZipEntry entry) {
    String name = entry.getName();
    int lastIndexOf = name.lastIndexOf(".");
    if (lastIndexOf == -1) {
        return "";
    }
    // 修正原逻辑,返回不带点的扩展名
    return name.substring(lastIndexOf + 1);
}

关键修改点

  • 使用getObbDir()获取应用专属OBB目录,替代硬编码的外部存储路径
  • 通过StorageManager异步挂载OBB文件,挂载成功后获取合法访问路径
  • 用FileProvider生成安全Uri,解决Android 7+的文件Uri暴露问题
  • 抽取重复的广播发送逻辑,提升代码可读性
  • 修正扩展名获取逻辑,避免生成冗余的带点文件名

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 10:34:56