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

如何为Android Service授予Uri读取权限以解决应用被杀后访问媒体文件报错问题

问题原因

通过ACTION_GET_CONTENT获取的媒体文件Uri仅持有临时访问权限,该权限与发起文件选择的Activity生命周期绑定,当应用进程被杀死后,系统会自动回收该临时权限,因此后台Service再访问Uri时就会触发SecurityException权限拒绝报错。

解决方案

1. 替换文件选择Action,申请持久化Uri权限

将原来的ACTION_GET_CONTENT替换为ACTION_OPEN_DOCUMENT,拿到Uri后主动申请持久化读取权限,权限不会随着应用进程销毁被回收:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult())
     { result ->
        if (result.resultCode == RESULT_OK) {
            val data: Intent? = result.data
            fileUri = data?.data!!
            // 申请持久化读取权限
            try {
                contentResolver.takePersistableUriPermission(
                    fileUri,
                    Intent.FLAG_GRANT_READ_URI_PERMISSION
                )
            } catch (e: SecurityException) {
                // 权限申请失败的异常处理,比如提示用户重新选择文件
                e.printStackTrace()
            }
        }
    }
    
    val myIntent = Intent()
                    .setType("audio/*")
                    // 替换为ACTION_OPEN_DOCUMENT支持持久化权限
                    .setAction(Intent.ACTION_OPEN_DOCUMENT)
                    // 申请读权限
                    .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

    resultLauncher.launch(myIntent)
}

2. 修正PendingIntent配置

针对Android 12及以上系统,需要明确指定PendingIntent的可变性标记,避免权限传递失效:

val alarmIntent = Intent(context,AlarmSetter::class.java)
alarmIntent.action = "SET_ALARM"
alarmIntent.putExtra("audio_path",fileUri.toString())

alarmManager = getSystemService(ALARM_SERVICE) as AlarmManager
// 明确指定PendingIntent标记,Android 12及以上必须指定可变性
val pendingFlag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
} else {
    PendingIntent.FLAG_UPDATE_CURRENT
}
var pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, pendingFlag)

alarmManager.setAndAllowWhileIdle(
    AlarmManager.RTC_WAKEUP,
    calendar.timeInMillis,
    pendingIntent
)

3. 补充必要的清单权限

在AndroidManifest.xml中添加前台服务、媒体读取、通知权限:

<!-- 前台服务基础权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- 媒体播放前台服务权限,Android 12+要求 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<!-- 通知权限,Android 13+要求 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- 音频读取权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<!-- 低版本兼容 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />

4. 确认前台服务配置

确保你Service中的notification()方法已经正确配置了前台服务通知渠道,符合Android 8.0及以上系统的前台服务要求,保证Service可以在后台正常运行。

修改完成后即可在应用被杀死的场景下,Service仍能正常读取Uri对应的音频文件播放。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 01:24:03