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

Android设备锁屏/APP被杀时,无需前台服务/Alarm Manager,如何通过推送播放警报音?

解决方案:确保紧急警报在所有场景下触发

针对你的紧急安全应用需求,要实现FCM推送在应用未运行、锁屏/休眠时播放警报音,需要从FCM消息类型、设备唤醒、音频权限、服务配置这几个维度优化,同时兼顾低耗电要求:

1. 切换使用FCM数据消息

FCM的通知消息在应用后台/未运行时会由系统直接展示通知,不会触发onMessageReceived;只有数据消息才会在任何应用状态下(包括被强制停止后)触发FirebaseMessagingService的回调。

服务器端发送格式示例:

{
  "to": "设备令牌",
  "data": {
    "type": "alarm",
    "sound": "emergency_alarm.mp3"
  },
  "priority": "high"
}

设置priority: high确保消息能快速送达,避免被系统延迟。

2. 唤醒休眠/锁屏设备

设备休眠时CPU处于低功耗状态,需要通过唤醒锁临时唤醒设备,保证警报能正常播放:

步骤:

  • 在AndroidManifest.xml中申请权限:
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <!-- 可选,用于解锁锁屏界面 -->
  • 在onMessageReceived中唤醒设备:
private void wakeUpDevice() {
    PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
    if (powerManager != null) {
        PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
            PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE,
            "MyApp:AlarmWakeLock"
        );
        wakeLock.acquire(30000); // 唤醒30秒,足够播放警报
    }

    // 可选:解锁锁屏
    KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
    if (keyguardManager != null) {
        KeyguardManager.KeyguardLock keyguardLock = keyguardManager.newKeyguardLock("MyApp:AlarmKeyguardLock");
        keyguardLock.disableKeyguard();
    }
}

3. 强制播放警报音(绕过锁屏静音)

锁屏或静音模式下,需要抢占音频焦点并强制播放:

步骤:

  • 申请音频权限:
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
  • 实现音频播放逻辑:
private void playAlarmSound() {
    AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    if (audioManager == null) return;

    // 保存当前音频状态,播放后恢复
    int originalVolume = audioManager.getStreamVolume(AudioManager.STREAM_ALARM);
    int originalMode = audioManager.getMode();

    // 强制设置警报音量为最大
    audioManager.setStreamVolume(AudioManager.STREAM_ALARM, audioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM), 0);
    // 设置音频模式,绕过静音
    audioManager.setMode(AudioManager.MODE_RINGTONE);

    // 请求音频焦点
    int result = audioManager.requestAudioFocus(
        null,
        AudioManager.STREAM_ALARM,
        AudioManager.AUDIOFOCUS_GAIN_TRANSIENT
    );

    if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
        MediaPlayer mediaPlayer = MediaPlayer.create(this, R.raw.emergency_alarm);
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
        mediaPlayer.setOnCompletionListener(mp -> {
            // 恢复原音频状态
            audioManager.setStreamVolume(AudioManager.STREAM_ALARM, originalVolume, 0);
            audioManager.setMode(originalMode);
            audioManager.abandonAudioFocus(null);
            mp.release();
            // 播放完成后停止前台服务(如果启动了)
            stopSelf();
        });
        mediaPlayer.start();
    }
}

4. 按需启动前台服务(避免持续耗电)

Android 8.0+对后台服务有严格限制,长时间后台操作会被系统杀死。但我们只在触发警报时临时启动前台服务,播放完成后立即停止:

实现代码:

private void startForegroundServiceForAlarm() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel channel = new NotificationChannel(
            "emergency_alarm",
            "紧急警报",
            NotificationManager.IMPORTANCE_LOW // 低优先级,避免干扰用户
        );
        NotificationManager notificationManager = getSystemService(NotificationManager.class);
        notificationManager.createNotificationChannel(channel);

        Notification notification = new NotificationCompat.Builder(this, "emergency_alarm")
            .setContentTitle("紧急警报激活")
            .setSmallIcon(R.drawable.ic_alarm)
            .setPriority(NotificationCompat.PRIORITY_LOW)
            .build();

        startForeground(1, notification);
    }
}

在onMessageReceived开头调用这个方法,警报播放完成后调用stopSelf()停止服务,不会持续占用资源。

5. 修正Service配置

你的Service配置存在语法错误(多余的引号),修正后确保系统能正确识别:

<service
    android:name=".notification.MyFirebaseMessagingService" <!-- 建议改名,避免和系统NotificationManager冲突 -->
    android:directBootAware="true"
    android:exported="true">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
        <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
    </intent-filter>
</service>

directBootAware="true"确保设备在直接启动模式(未解锁)下也能触发服务。

6. 应对厂商后台限制

国内部分厂商(如小米、华为、vivo)有严格的后台进程管理,即使配置正确,应用被强制停止后可能无法接收FCM。你需要在应用中引导用户手动开启:

  • 自启动权限
  • 后台运行权限
  • 忽略电池优化
    可以在应用首次启动时弹出引导页面,说明这些权限对紧急警报功能的必要性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 02:20:28