You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Flutter Android前台音频服务在应用被终止时意外停止的问题求助

Flutter Android前台音频服务在应用被终止时意外停止的问题求助

大家好,我最近在开发一个Flutter Android的前台音频流服务,用Kotlin结合ExoPlayer实现直播音频播放,同时通过系统通知栏展示媒体控制按钮。目前应用在前台或后台挂起时都能正常工作,但一旦把应用从最近应用列表划掉,或者进程被系统杀死,音频服务就跟着停了,通知栏的控制通知也直接消失了——这完全不符合我对前台服务应该持续运行的预期。

我已经配置了必要的权限,也按照前台服务的要求完成了核心逻辑,但问题还是没解决,想请各位大佬帮忙排查下可能的问题。

已实现的核心内容

1. AndroidManifest.xml配置

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>

    <application 
        android:label="Simple Radio" 
        android:name="${applicationName}" 
        android:icon="@mipmap/ic_launcher">
        <activity 
            android:name=".MainActivity" 
            android:exported="true" 
            android:launchMode="singleTop" 
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:windowSoftInputMode="adjustResize">
            <meta-data 
                android:name="io.flutter.embedding.android.NormalTheme" 
                android:resource="@style/NormalTheme"/>
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <service 
            android:name=".AudioPlayerService" 
            android:foregroundServiceType="mediaPlayback" 
            android:exported="true" 
            android:enabled="true"/>
        <receiver 
            android:name=".NotificationActionReceiver" 
            android:exported="true">
            <intent-filter>
                <action android:name="ACTION_PLAY"/>
                <action android:name="ACTION_PAUSE"/>
                <action android:name="ACTION_STOP"/>
            </intent-filter>
        </receiver>
        <meta-data android:name="flutterEmbedding" android:value="2" />
        <meta-data android:name="firebase_messaging_auto_init_enabled" android:value="false" />
        <meta-data android:name="firebase_analytics_collection_enabled" android:value="false" />
    </application>
</manifest>

2. MainActivity代码(Flutter与原生通信部分)

package com.example.simple_radio

import android.content.Intent
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.EventChannel

class MainActivity : FlutterActivity() {
    private val CHANNEL = "audio.channel"
    private val EVENT_CHANNEL = "audio.events"

    companion object {
        var eventSink: EventChannel.EventSink? = null
    }

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            when (call.method) {
                "play" -> {
                    val url = call.argument<String>("url") ?: ""
                    val title = call.argument<String>("title") ?: "Live Stream"
                    val image = call.argument<String>("image") ?: "Live Stream"
                    val intent = Intent(this, AudioPlayerService::class.java).apply {
                        action = "ACTION_PLAY"
                        putExtra("url", url)
                        putExtra("title", title)
                        putExtra("image", image)
                    }
                    startForegroundService(intent)
                    result.success(null)
                }
                "pause" -> {
                    val intent = Intent(this, AudioPlayerService::class.java).apply {
                        action = "ACTION_PAUSE"
                    }
                    startService(intent)
                    result.success(null)
                }
                "stop" -> {
                    val intent = Intent(this, AudioPlayerService::class.java).apply {
                        action = "ACTION_STOP"
                    }
                    startService(intent)
                    result.success(null)
                }
                "getStatus" -> {
                    val intent = Intent(this, AudioPlayerService::class.java).apply {
                        action = "GET_STATUS"
                    }
                    startService(intent)
                    result.success(null)
                }
                else -> result.notImplemented()
            }
        }

        EventChannel(flutterEngine.dartExecutor.binaryMessenger, EVENT_CHANNEL).setStreamHandler(
            object : EventChannel.StreamHandler {
                override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
                    eventSink = events
                }

                override fun onCancel(arguments: Any?) {
                    eventSink = null
                }
            }
        )
    }
}

3. AudioPlayerService(部分代码)

package com.example.simple_radio

import android.app.*
import android.content.Context
import android.content.Intent
import android.os.IBinder
import androidx.core.app.NotificationCompat

// 注:完整代码尚未粘贴,核心逻辑已包含:
// 1. 初始化ExoPlayer并加载音频流
// 2. 在onStartCommand中创建媒体风格通知
// 3. 调用startForeground(NOTIFICATION_ID, notification)将服务设为前台
// 4. 实现了播放、暂停、停止的逻辑处理

我的疑问和排查点

我已经做了这些操作:

  • 申请了所有必要的权限:INTERNETPOST_NOTIFICATIONSFOREGROUND_SERVICEWAKE_LOCK
  • 给服务指定了foregroundServiceType="mediaPlayback"
  • startForegroundService()启动服务,并且在服务内部及时调用了startForeground()绑定通知
  • 通知用的是MediaStyle,包含了播放/暂停/停止的控制按钮

但为什么应用从最近列表划掉后,前台服务还是会被杀死?会不会是:

  1. 我的onStartCommand返回值有问题?我目前返回的是START_STICKY,是不是应该用START_REDELIVER_INTENT
  2. 厂商的后台限制?比如小米、华为这类厂商的“纯净模式”或者后台管理策略?
  3. 通知的配置有问题?比如没有设置正确的ContentIntent或者通知渠道?
  4. ExoPlayer的配置有没有遗漏?比如没有设置WakeLock或者AudioAttributes

有没有大佬能帮我分析下可能的原因,或者给我一些排查的方向?非常感谢!

内容来源于stack exchange

火山引擎 最新活动