PAYATHON 2026

Expo React Native 如何在 Android 后台播放约30秒的通知声音?

数据小陈

能否只靠 expo-notifications 实现?

expo-notifications 可以配置较长的自定义通知声音,但在 Android 上,应用处于后台或进程终止时,无法保证所有设备都持续播放约 30 秒。通知 API 也没有保证播放时长或持续循环的控制接口。如果 30 秒告警是核心需求,需要评估原生音频播放与 foreground service 方案。

目前的信息不足以判断声音为什么在 3 至 5 秒后停止,不能直接将其归因于 Android 或 Expo 的统一时长限制。Expo Notifications 文档

先检查通知声音是否配置正确

Android 8.0 及以上版本主要通过 notification channel 管理通知声音。请确认声音文件已打包进应用,渠道引用了正确的文件,推送也使用了该渠道。

假设项目已有 assets/alert.wav,在 app.json 的现有配置中加入:

{
  "expo": {
    "plugins": [
      [
        "expo-notifications",
        {
          "sounds": ["./assets/alert.wav"]
        }
      ]
    ]
  }
}

修改后需要重新构建并安装应用,让音频进入原生安装包。然后在 Android 应用初始化时创建渠道,并等待创建完成:

import { Platform } from 'react-native';
import * as Notifications from 'expo-notifications';

export async function configureAlertChannel() {
  if (Platform.OS !== 'android') return;

  await Notifications.setNotificationChannelAsync('emergency_alerts_v1', {
    name: '紧急告警',
    importance: Notifications.AndroidImportance.HIGH,
    sound: 'alert.wav',
  });
}

这里使用文件名 alert.wav,不用项目中的完整路径。这些配置用于选择通知声音,不能保证声音完整播放 30 秒。Expo 自定义通知声音说明

如果通过 Expo Push Service 发送 Android 推送,请指定已经创建的 channelId:

{
  "to": "ExponentPushToken[YOUR_TOKEN]",
  "title": "紧急告警",
  "body": "请立即查看告警详情",
  "channelId": "emergency_alerts_v1",
  "priority": "high"
}

priority: "high" 影响消息投递优先级,不会延长声音播放时间。如果渠道不存在,指定该渠道的通知也无法正常显示。Expo 推送参数说明

还要检查系统中的实际渠道设置。渠道创建后,应用不能通过再次设置同一个 ID 来覆盖声音等行为,用户也可以修改这些设置。开发测试时,可以用新的渠道 ID 验证配置变更;正式使用时应尊重用户的选择。Android notification channel 文档

后台、进程终止和强行停止有什么区别?

这几种状态需要分别验证:

  • 应用处于前台:可以在应用内使用音频播放器控制播放和停止。
  • 应用处于后台或进程已终止:普通通知消息通常由系统展示,不能假设常规 JavaScript 接收监听器会执行并维持播放。
  • 用户在系统设置中强行停止应用:需要用户重新打开应用,通知才能恢复工作,不能承诺通过推送自动启动告警。Expo 通知类型与应用状态说明

Notifications.registerTaskAsync() 可以配合适当的 Headless Background Notification,在后台或进程终止后执行任务。不过,即使任务被触发,也无法保证声音连续播放 30 秒。操作系统仍可能限制消息交付和后台执行。Expo 后台通知任务说明

需要持续告警时,应该怎样设计?

如果需要在收到消息后自动响铃约 30 秒,应把音频播放交给原生组件管理,并评估使用 foreground service。可以按以下顺序实现:

  1. 设计一条推送消息路径,使原生接收端能够在后台处理消息。
  2. 收到有效告警后,检查是否满足后台启动 foreground service 的条件。
  3. 启动服务并展示服务通知,由原生播放器播放本地音频;需要时循环播放。
  4. 使用原生计时机制在约 30 秒后停止,同时提供用户可操作的停止入口。
  5. 处理重复告警、播放失败和用户停止,并释放播放器和服务资源。

这条路径仍受系统条件限制。对于 target Android 12 及以上的应用,后台启动 foreground service 受到限制。实际收到的高优先级 FCM 消息属于例外之一,但消息可能被降级,因此需要检查接收时的实际优先级并处理启动失败。Android 后台启动限制

还需要根据实际用途和目标版本声明相应的 foreground service 类型及权限;持续音频播放应评估 mediaPlayback 类型。Android foreground service 类型说明

使用原生方案是否必须放弃 Expo?

不必迁移整个项目。Expo 支持通过带原生代码的库、config plugin 或自定义 Expo module 扩展功能,再生成 development build 和正式安装包。增加原生能力需要重新构建,不能仅靠 JavaScript 更新完成。Expo 添加原生代码指南

如果必须只使用现有的 expo-notifications 能力,可以先修正声音和渠道配置,在目标设备上验证较长音频的实际表现,并在需求描述中保留设备差异。如果必须自动持续告警,则需要完成原生接收、服务和播放器的整条链路。

如何排查当前的 3 至 5 秒截断?

先确认音频文件本身有完整的有效声音,再确认设备播放的是该文件,而不是系统默认提示音。随后分别测试前台、后台、锁屏、进程终止和系统强行停止的情况,记录 Android 版本、设备型号、渠道设置及是否实际收到消息。

同时检查通知是否被取消或替换,以及截断是否只发生在特定设备。没有音频文件、推送 payload 和设备信息时,无法准确归因,也不宜通过反复发送通知来拼接 30 秒声音。

备注:内容仅供参考。