禁用 NotificationChannel 的声音

100

今天我开始针对API 26进行开发,这迫使我使用通知渠道。

我的问题是,现在每次新通知(包括更新)都会播放一个恼人的声音。

如何禁用这个声音?

我尝试将此声音替换为自定义mp3声音,然后传递一个带有静音的mp3给它,但被忽略了。

我只是添加了一个非常低优先级的通知,基本上给用户提供了与应用程序交互后执行一些操作的选项。没有理由让它变成大声的,用户会知道他可以参考通知,因为他已经做了一定的事情,他知道会引起通知的出现。

用户确实会因为那个声音而感到烦恼。


这回答解决了您的问题吗?如何在Java中显示无声通知 - Marvin
18个回答

145

如果您想保留频道的重要性,只是想删除声音,notificationChannel.setSound(null, null); 似乎可以解决问题。

编辑:请确保更改频道 ID(并删除旧频道),以便将其应用于现有用户。 (应用程序无法修改频道,只能由用户进行修改。)


21
卸载/重新安装,或者只是重命名通知渠道(NotificationChannel)。 - Bjarte Aune Olsen
1
我认为更好的解决方案是使用NotificationManager.IMPORTANCE_DEFAULT。这将禁用声音,并且对于重要性来说是一个不错的选择。请注意,如果您使用NotificationManager.IMPORTANCE_HIGH并且多次显示相同的通知,则它将继续弹出。使用IMPORTANCE_DEFAULT还可以防止再次弹出相同的通知。 - Markymark
21
我使用了NotificationManager.IMPORTANCE_DEFAULT,但是在创建通知时仍然有声音。 - user924
4
在我的音乐应用中,NotificationManager.IMPORTANCE_LOW 对我有用,可以禁用声音。 - Akash Bisariya
2
使用AndroidX库会显示一个辅助方法NotificationCompat.Builder.setSilent,该方法有效地将声音设置为null。 - Marvin
显示剩余5条评论

105

(2019 年 5 月更新:使用 NotificationManager.IMPORTANCE_LOW,并为其创建一个新频道,这样才能避免在 Android Q 上出现问题,至少在模拟器中会有声音...)


解决方法是使用 NotificationManager.IMPORTANCE_LOW 并为其创建一个新的通道。一旦创建了通道,就无法更改其重要性(当然你可以尝试更改,但新的重要性将被忽略)。系统似乎会永久存储通道信息,并且仅在卸载应用程序时才会删除任何已创建的通道。[更新:根据Ferran Negre的评论,您可以通过 nm.deleteNotificationChannel(nChannel.getId()); 删除通道,并使用 nm.createNotificationChannel(nChannel); 重新创建它,但明显存在一个限制,即不能创建具有已删除通道相同 ID 的通道,并期望能够对未删除的通道应用不同的设置,参见 acoder 的回答]

之前的 Android 版本默认情况下没有播放声音,但自 Android O 起发生了变化,只有在目标 API 26(使用通知频道)时才会发出声音。这是一种不一致性,实际上是一个 bug:

造成这种情况的原因是当你使用 NotificationManager.IMPORTANCE_DEFAULT(默认情况下不需要声音)创建通道时,Android 实际上将其“某种程度上”注册为 NotificationManager.IMPORTANCE_HIGH(默认情况下播放声音)。

可以通过进入通知选项(长按通知条目)来检查此问题,在那里您将看到它是 NotificationManager.IMPORTANCE_HIGH 类型,然后禁用通知并重新启用通知。在此过程中,它从 NotificationManager.IMPORTANCE_HIGH "降级" 到非声音的实际注册状态 NotificationManager.IMPORTANCE_DEFAULT

该 bug 已提交到 Android 问题跟踪器,请对其进行标记(由 Google 标记为“无法修复(不可行)”,因为... 特权)。


顺便说一句,https://developer.android.com/training/notify-user/channels 上的新文档声称默认行为以前就是这样,即在 Android 8.0 之前,default播放声音,这是绝对不正确的。以下是它们的列表:

User-visible importance level           Importance               Priority   
                                        (Android 8.0 and higher) (Android 7.1 and lower)
Urgent  Makes a sound and appears as    IMPORTANCE_HIGH          PRIORITY_HIGH
        a heads-up notification                                  or PRIORITY_MAX
High    Makes a sound                   IMPORTANCE_DEFAULT       PRIORITY_DEFAULT
Medium  No sound                        IMPORTANCE_LOW           PRIORITY_LOW
Low     No sound and does not appear    IMPORTANCE_MIN           PRIORITY_MIN
        in the status bar
你甚至可以看到可见性重要性高和通知重要性高之间的不匹配...我不知道他们为什么这样做。他们在代码中肯定有一个bug。以下所有内容都已过时,但提到的错误仍然有效。我的错误是认为NotificationManager.IMPORTANCE_MIN是从NotificationManager.IMPORTANCE_DEFAULT开始的下一个更低级别,但实际上是NotificationManager.IMPORTANCE_LOW。当您通过长按通知并切换该通道的所有频道按钮进入应用程序的通知设置后,再将该通道的开关关闭并再次打开时,它实际上会将自身设置为NotificationManager.IMPORTANCE_DEFAULT,并且不会播放任何声音。我还注意到,在崩溃之后,它被重置为NotificationManager.IMPORTANCE_HIGH。因此,基本上解决方法是使用NotificationManager.IMPORTANCE_MIN。但是,您必须创建一个新的通道,以便此NotificationManager.IMPORTANCE_MIN生效,因为一旦创建了一个通道,似乎就无法更改其重要性。更新:结果发现使用NotificationManager.IMPORTANCE_MIN的解决方法具有缺点。当您使用该重要性级别时,通知将不再完全显示在通知抽屉中,而是插入一个默认情况下折叠的新通知通道组中(每次拉下抽屉时都会重新折叠)。真糟糕!更新2:深入挖掘后,发现这好像是正确注册为NotificationManager.IMPORTANCE_DEFAULT,但某种方式它神奇地升级到NotificationManager.IMPORTANCE_HIGH,就像当用户将设置从默认设置更改为高级设置时一样。关闭通知然后再次打开后,该选项也会重置为默认值。

11
重要提示:一旦创建了通知渠道,就无法更改其重要性(当然可以更改,但新的重要性将被忽略)。我曾经为此苦苦挣扎了很长时间,因为我不明白对 NotificationChannel 进行的任何更改都没有反映在应用程序行为上的原因。我不得不每次对通道进行更改时重新命名通道以查看更改。 - Bjarte Aune Olsen
新频道ID还是新频道名称,或者两者都有? - user924
1
我设置了 IMPORTANCE_DEFAULT 并且声音被播放了。 - user924
很遗憾看到这样一个简单的错误仍然没有被修复。感谢您记录下所有细节,我也经历了同样的痛苦。 - Kira
1
我认为如果你在执行 nm.deleteNotificationChannel(nChannel.getId());nm.createNotificationChannel(nChannel); 之后再次执行,那么 NotificationManager.IMPORTANCE_LOW 将会生效(对我来说是有效的)。 - Ferran Negre
@FerranNegre 谢谢你提供的信息!我想我还没有尝试过这个。也许有人可以确认一下这是否有效? - Daniel F

15

那么我将添加一个完整的答案来帮助解决问题。如果您阅读androidx中的NotificationCompat代码。

   /**
     * Silences this instance of the notification, regardless of the sounds or vibrations set
     * on the notification or notification channel.
     */
    public @NonNull Builder setNotificationSilent() {
        mSilent = true;
        return this;
    }

如果你想同时移除声音和振动,就需要像这样使用: if you want remove sound AND vibration
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
            // no sound or vibration
            .setNotificationSilent()

如果您想要“仅删除声音”,这是方法。
 // no sound
 builder.setSound(null);

如果你只希望移除震动,请执行以下操作。
 // no vibration
 mChannel.setVibrationPattern(new long[]{ 0 });
 mChannel.enableVibration(true);

2
对我来说,setSilent 也会使通知不再弹出,这不是我想要的。 setSound(null) 的效果符合预期。 - Florian Walther
1
这已被废弃。 - Ollie C

9

3
听起来是个有用的答案。我只想指出这是 Android Jetpack 库(androidx) 的一部分,而我们大多数人可能已经在使用它了。 - Daniel F
这是适用于Android 12的可行解决方案。 - electroid
@electroid NotificationCompat.Builder是AndroidX库的一部分,具有向后兼容性。 - Marvin

8
自API 26(奥利奥)以来,据我所见,一旦创建通知后就无法更改其声音。
    notificationManager.deleteNotificationChannel("channel_id"));
    NotificationChannel notificationChannel = new NotificationChannel(
        "channel_id", "channel_name",
        NotificationManager.IMPORTANCE_HIGH);
    notificationChannel.setSound(null, null);
    notificationManager.createNotificationChannel(notificationChannel);

即使在创建通道之前删除通道也无济于事。
Google文档说明:
android.app.NotificationManager public void deleteNotificationChannel(String channelId)
删除给定的通知渠道。如果您使用相同的ID创建一个新通道,则已删除的通道将被取消删除,并具有其删除之前具有的所有设置。
NotificationChannel#setSound()文档说明
仅在将通道提交到NotificationManager#createNotificationChannel(NotificationChannel)之前可修改。
很遗憾,notificationBuilder.setSound(defaultSoundUri)也不起作用:
该方法在API级别26中已弃用。请改用NotificationChannel#setSound(Uri, AudioAttributes)。
使用支持库也无法解决此问题。因此,声音只能在应用程序中设置一次,并且用户更改声音只能在通知的设置中进行。对我来说,Ferran Negre's 的评论没有起作用。我不明白为什么Google要做出这种限制。太糟糕了。

您可以使用不同的频道ID创建新的频道,并将通知分配给该频道。 - Razi Kallayi

5

我测试了许多安卓设备,以下代码对我有效。

首先,创建一个通知构建器(notificationBuilder),如果您的Build.Version大于26,请添加一个新渠道(channel)。

  private val notificationBuilder: NotificationCompat.Builder by lazy {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) NotificationCompat.Builder(context) else {
            val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            val channelId = "MUSIC"
            val channelName = "音乐控制栏"
            val importance = NotificationManager.IMPORTANCE_MIN
            val channel = NotificationChannel(channelId, channelName, importance)

            manager.createNotificationChannel(channel)
            channel.enableLights(false)
            channel.vibrationPattern = longArrayOf(0L)
            channel.enableVibration(false)
            channel.setSound(null, null)
            NotificationCompat.Builder(context, channelId)
        }

    }

其次,初始化该notificationBuilder,并将声音设置为null。
   notificationBuilder.setDefaults(Notification.DEFAULT_LIGHTS ).setVibrate( longArrayOf(0L)).setSound(null)

第三,如果build.version大于24,请设置其优先级。

  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            notificationBuilder.priority = NotificationManager.IMPORTANCE_MIN
        }

希望这对您有用。

4
NotificationManager.IMPORTANCE_LOW

当我需要在我的音乐应用中创建通知时,它不会发出声音。

如果您已经创建了一个通知渠道,则需要更改渠道ID或仅卸载先前的应用程序并重新安装。


3
谢谢。让这个方法生效的关键信息不仅是实施上述步骤,而且还需要卸载并重新安装应用程序。 - TomV

3

对我来说解决方案是创建群组通知

val builder = NotificationCompat.Builder(this)
        .setGroupAlertBehavior(GROUP_ALERT_SUMMARY)
        .setGroup("My Group")
        .setGroupSummary(false)
        .setDefaults(DEFAULT_ALL)
        .setSound(null)

但是在这种情况下,如果您发送一个新的通知并带有新的ID,那么它将与先前的通知分组。


2

2
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val notificationChannel = NotificationChannel(
                channelId.toString(), title,
               NotificationManager.IMPORTANCE_DEFAULT
            )
            notificationChannel.setSound(null,null)
            notificationChannel.enableVibration(false)
            notificationChannel.description = body
            if(notificationManager.getNotificationChannel(channelId.toString())==null) {
                notificationManager.createNotificationChannel(notificationChannel)
            }
            if (data["sound"]?.equals("default", true) == true) {//if your app need contorl sound enable
                RingtoneManager.getRingtone(
                    this,
                    RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
                ).play()
            }
            if(pushShake.isTrue() ){//if your app need contorl vibarate enable
               val vbmanager=  getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
                vbmanager.vibrate(VibrationEffect.createOneShot(500,VibrationEffect.DEFAULT_AMPLITUDE))
            }
        }

以下代码涉及通知,但在API 26上不会播放声音或震动,因此不必担心设置声音或振动。

notificationManager.notify(channelId.toInt(), notificationBuilder.apply {
            setContentIntent(pendingIntent)
            setSmallIcon(R.drawable.img_logo)
            setTicker(title)
            setNumber(data["badge"]?.toIntOrNull() ?: 0)
            setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL)
            color = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
                resources.getColorMuteDepre(R.color.colorAccent2)
            } else {
                Color.parseColor("#ffffff")
            }


            setContentTitle(title)
            setContentText(body)
            setWhen(System.currentTimeMillis())
            setAutoCancel(true)
            setSound(null)
            setVibrate(longArrayOf())
            if (pushShake.isTrue() &&  data["sound"]?.equals("default", true) == true) {
                setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
                    val vbmanager = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
                    vbmanager.vibrate(500)
                }
            }else{
                if (data["sound"]?.equals("default", true) == true) {
                    setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
                }
                if (pushShake.isTrue() ) {
                    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
                        val vbmanager = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
                        vbmanager.vibrate(500)
                    }
                }
            }


            setStyle(
                NotificationCompat.BigTextStyle().bigText(body).setSummaryText(body).setBigContentTitle(
                    title
                )
            )
            setPriority(NotificationCompat.PRIORITY_DEFAULT)

        }.build())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接