如何为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
相关产品推荐
相关产品推荐

