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

Android:音频Uri存入SharedPreferences后MediaPlayer调用setDataSource失败

问题分析与解决办法

我来帮你梳理下遇到的问题,以及对应的修复方案:

1. 核心问题:错误的Uri编码操作

你在LoadSettings()里调用了Uri.encode(str)再解析,这是完全多余的操作!uri.toString()本身就是该Uri的标准字符串表示,强行编码会把content://这类格式转成content%3A//,导致解析后的Uri完全无效,自然找不到音频资源。

修正后的LoadSettings方法:

private Uri LoadSettings() {
    SharedPreferences pref = getSharedPreferences(APP, MODE_PRIVATE);
    String str = pref.getString("uri", null);
    if(str == null) {
        return null;
    }
    // 直接解析原始字符串,不需要编码
    return Uri.parse(str);
}

2. 隐藏坑:Content Uri的持久化权限

通过Intent.ACTION_PICK获取的音频Uri大多是content://开头的内容Uri,这类Uri的访问权限是临时的——App重启后,系统会收回这个临时权限,导致你即使解析出正确的Uri,也无法访问对应的资源。

解决办法:申请持久化Uri权限
在你获取到Uri的onActivityResult方法里,调用ContentResolver.takePersistableUriPermission()把临时权限转为持久权限:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK && requestCode == YOUR_PICK_REQUEST_CODE) {
        Uri selectedUri = data.getData();
        if (selectedUri != null) {
            // 获取Intent中的权限标记
            int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            // 申请持久化权限
            getContentResolver().takePersistableUriPermission(selectedUri, takeFlags);
            // 再保存Uri
            SaveSettings(selectedUri);
        }
    }
}

注:这个操作不需要在Manifest中额外声明权限,因为ACTION_PICK已经隐含了临时访问权限,我们只是把它转为持久有效。

3. MediaPlayer的优化建议

你的PlaySound方法存在资源泄漏和阻塞UI的风险,建议做以下调整:

private void PlaySound() {
    // 复用MediaPlayer实例,避免频繁创建
    if (_mp == null) {
        _mp = new MediaPlayer();
        _mp.setOnErrorListener((mp, what, extra) -> {
            Log.e("MediaPlayer", "播放错误: code=" + what + ", extra=" + extra);
            mp.release();
            _mp = null;
            return true;
        });
    }

    if (!_paused) {
        try {
            _mp.reset();
            _mp.setDataSource(this, _uri);
            // 用异步prepare避免阻塞UI线程
            _mp.prepareAsync();
            _mp.setOnPreparedListener(MediaPlayer::start);
        } catch (IOException e) {
            Log.e("PlaySound", "设置数据源失败", e);
        }
    } else {
        _mp.start();
    }
    _paused = false;
}

另外,记得在App销毁时释放MediaPlayer资源:

@Override
protected void onDestroy() {
    super.onDestroy();
    if (_mp != null) {
        _mp.release();
        _mp = null;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 06:51:19