Kotlin安卓闹钟更换外部存储音频后仍播放旧音频的问题求助
问题描述
我正在用Kotlin开发一款安卓闹钟实验应用,希望能从外部存储更换闹钟音频。已设置外部存储中的音频A作为闹钟音,之后重新选择外部存储中的音频B替换,但触发闹钟时仍播放旧音频A而非新音频B。
相关代码
选择音频按钮
binding.selectAudio.setOnClickListener() { if (checkPermission()) { openAudio() } else { requestPermission() } }
跳转选择音频并获取路径的逻辑
private fun openAudio() { //soundPath = null onStop() val openFileIntent = Intent(Intent.ACTION_GET_CONTENT) openFileIntent.type = "audio/*" getResult.launch(openFileIntent) }
private val getResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { soundPath = null if (it.resultCode == RESULT_OK) { val uri: Uri? = it.data!!.data File(uri!!.getPath()) val path = getRealPath(this, uri) soundPath = path if (soundPath != null) { Toast.makeText(this, "Audio file has been set", Toast.LENGTH_SHORT).show() } } }
设置闹钟按钮
binding.setAlarmBtn.setOnClickListener() { createNotificationChannel(soundPath) setAlarm() Log.e("notif", "soundpath" + soundPath.toString()) Log.d("soundPath", soundPath.toString()) }
设置闹钟时间的逻辑
private fun setAlarm() { try { alarmManager = getSystemService(ALARM_SERVICE) as AlarmManager val intent = Intent(this, AlarmReceiver::class.java) pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_IMMUTABLE) alarmManager[AlarmManager.RTC_WAKEUP, calendar.timeInMillis] = pendingIntent Toast.makeText(this, "Alarm set successfully", Toast.LENGTH_SHORT).show() } catch (e: Exception) { Toast.makeText(this, "Time is not been set", Toast.LENGTH_SHORT).show() } }
创建通知频道(关联音频)的逻辑
private fun createNotificationChannel(path: String?) { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val uri = getUri(path) val attributes = AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM).build() val name : CharSequence = "AlarmReminderChannel" val description = "Channel For Alarm Manager" val importance = NotificationManager.IMPORTANCE_HIGH val channel = NotificationChannel("alarmManagerDemo", name, importance) channel.description = description channel.setSound(uri, attributes) Log.e("notif", "uri" + uri.toString()) Log.e("notif", "uri from channel" + channel.sound.toString()) val notificationManager = getSystemService(NotificationManager::class.java) notificationManager.createNotificationChannel(channel) } }
获取音频Uri的逻辑
private fun getUri(path: String?) : Uri? { Log.e("notif", "path: "+path ) if(path == null) { Log.e("notif", "null" ) return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + this.packageName + "/raw/music") } else { Log.e("notif", "tidak null" ) return Uri.parse(path) } }
广播接收器的onReceive逻辑
class AlarmReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { val bundle : Bundle? = intent!!.extras val i = Intent(context, DestinationActivity::class.java) intent!!.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK val pendingIntent = PendingIntent.getActivity(context, 0, i, PendingIntent.FLAG_IMMUTABLE) val builder = NotificationCompat.Builder(context!!, "alarmManagerDemo") .setSmallIcon(R.drawable.ic_launcher_background) .setContentTitle("Demo") .setContentText("Bla bla bla bla bla bla bla bla") .setAutoCancel(true) .setDefaults(NotificationCompat.DEFAULT_ALL) .setPriority(NotificationCompat.PRIORITY_HIGH) .setContentIntent(pendingIntent) val notificationManager = NotificationManagerCompat.from(context) notificationManager.notify(123, builder.build()) } }
问题原因
- 通知频道属性不可修改:Android O及以上版本,通知频道创建后,声音等核心属性无法直接更新,重复调用
createNotificationChannel不会覆盖已有配置,导致旧音频设置保留。 - PendingIntent未更新:原代码中创建PendingIntent时未使用更新标记,可能导致广播接收器无法获取最新的音频配置。
- 文件路径处理存在风险:通过
getRealPath获取的本地路径在Android 10+上可能因存储权限限制无法访问,且路径转Uri的方式容易引发权限问题。
解决方案
1. 删除旧通知频道后重建
修改createNotificationChannel函数,每次创建前删除已存在的频道,确保新配置生效:
private fun createNotificationChannel(path: String?) { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val notificationManager = getSystemService(NotificationManager::class.java) // 先删除旧频道 notificationManager.deleteNotificationChannel("alarmManagerDemo") val uri = getUri(path) val attributes = AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM).build() val name: CharSequence = "AlarmReminderChannel" val description = "Channel For Alarm Manager" val importance = NotificationManager.IMPORTANCE_HIGH val channel = NotificationChannel("alarmManagerDemo", name, importance) channel.description = description channel.setSound(uri, attributes) Log.e("notif", "uri" + uri.toString()) Log.e("notif", "uri from channel" + channel.sound.toString()) notificationManager.createNotificationChannel(channel) } }
2. 更新PendingIntent创建逻辑
使用FLAG_UPDATE_CURRENT标记确保PendingIntent更新,同时传递音频路径到广播接收器:
private fun setAlarm() { try { alarmManager = getSystemService(ALARM_SERVICE) as AlarmManager val intent = Intent(this, AlarmReceiver::class.java) // 传递音频路径到接收器 intent.putExtra("SOUND_PATH", soundPath) // 确保PendingIntent更新 pendingIntent = PendingIntent.getBroadcast( this, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) alarmManager[AlarmManager.RTC_WAKEUP, calendar.timeInMillis] = pendingIntent Toast.makeText(this, "Alarm set successfully", Toast.LENGTH_SHORT).show() } catch (e: Exception) { Toast.makeText(this, "Time is not been set", Toast.LENGTH_SHORT).show() } }
3. 优化音频Uri存储方式
直接存储选择音频时得到的Content Uri,避免文件路径转换的权限问题:
修改getResult回调:
private val getResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { soundPath = null if (it.resultCode == RESULT_OK) { val uri: Uri? = it.data?.data if (uri != null) { // 直接存储Uri字符串 soundPath = uri.toString() Toast.makeText(this, "Audio file has been set", Toast.LENGTH_SHORT).show() } } }
4. 可选:在广播接收器中直接播放音频
如果依赖通知频道不够灵活,可在接收器中用MediaPlayer直接播放最新音频:
class AlarmReceiver : BroadcastReceiver() { private var mediaPlayer: MediaPlayer? = null override fun onReceive(context: Context?, intent: Intent?) { val soundPath = intent?.getStringExtra("SOUND_PATH") // 播放自定义音频 soundPath?.let { try { mediaPlayer = MediaPlayer().apply { setAudioAttributes( AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_ALARM) .build() ) setDataSource(context, Uri.parse(it)) prepare() start() } } catch (e: Exception) { e.printStackTrace() } } val i = Intent(context, DestinationActivity::class.java) i.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK val pendingIntent = PendingIntent.getActivity(context, 0, i, PendingIntent.FLAG_IMMUTABLE) val builder = NotificationCompat.Builder(context!!, "alarmManagerDemo") .setSmallIcon(R.drawable.ic_launcher_background) .setContentTitle("Demo") .setContentText("Bla bla bla bla bla bla bla bla") .setAutoCancel(true) .setPriority(NotificationCompat.PRIORITY_HIGH) .setContentIntent(pendingIntent) // 移除默认声音,避免冲突 // .setDefaults(NotificationCompat.DEFAULT_ALL) val notificationManager = NotificationManagerCompat.from(context) notificationManager.notify(123, builder.build()) // 播放完毕释放资源 mediaPlayer?.setOnCompletionListener { it.release() mediaPlayer = null } } }
内容的提问来源于stack exchange,提问作者Pup In The Tree
相关产品推荐
相关产品推荐

