From 91240b2de523c4667b469c667effd7accb871f70 Mon Sep 17 00:00:00 2001 From: shou Date: Tue, 3 Feb 2026 12:06:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=89=8D=E5=8F=B0=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E6=9C=8D=E5=8A=A1=E4=BB=A5=E6=94=AF=E6=8C=81=E7=9B=B4?= =?UTF-8?q?=E6=92=AD=E6=92=AD=E6=94=BE=EF=BC=8C=E6=9B=B4=E6=96=B0=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E6=9D=83=E9=99=90=E5=92=8C=E9=80=9A=E7=9F=A5=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/src/main/AndroidManifest.xml | 6 ++ .../SellyCloudSDK/live/LivePlayActivity.kt | 11 +++ .../live/LivePlayForegroundService.kt | 85 +++++++++++++++++++ example/src/main/res/values/strings.xml | 2 + 4 files changed, 104 insertions(+) create mode 100644 example/src/main/java/com/demo/SellyCloudSDK/live/LivePlayForegroundService.kt diff --git a/example/src/main/AndroidManifest.xml b/example/src/main/AndroidManifest.xml index ccf03a3..9f2d07e 100644 --- a/example/src/main/AndroidManifest.xml +++ b/example/src/main/AndroidManifest.xml @@ -12,6 +12,7 @@ + @@ -75,6 +76,11 @@ android:exported="false" android:foregroundServiceType="camera|microphone|mediaProjection" /> + + diff --git a/example/src/main/java/com/demo/SellyCloudSDK/live/LivePlayActivity.kt b/example/src/main/java/com/demo/SellyCloudSDK/live/LivePlayActivity.kt index 4add4ae..c01094b 100644 --- a/example/src/main/java/com/demo/SellyCloudSDK/live/LivePlayActivity.kt +++ b/example/src/main/java/com/demo/SellyCloudSDK/live/LivePlayActivity.kt @@ -244,6 +244,7 @@ class LivePlayActivity : AppCompatActivity() { if (isPlaying) { logEvent("用户操作: 暂停") playerClient.pause() + stopPlaybackForegroundService() return } logEvent("用户操作: 播放") @@ -272,6 +273,7 @@ class LivePlayActivity : AppCompatActivity() { if (hasReleasedPlayer) return hasReleasedPlayer = true playerClient.release() + stopPlaybackForegroundService() } private fun finishForNewPlayback() { @@ -343,12 +345,21 @@ class LivePlayActivity : AppCompatActivity() { } private fun beginPlayback() { + startPlaybackForegroundService() startPlayAttempt() resetPreviewForPlayback() playerClient.prepareToPlay() playerClient.play() } + private fun startPlaybackForegroundService() { + LivePlayForegroundService.start(this) + } + + private fun stopPlaybackForegroundService() { + LivePlayForegroundService.stop(this) + } + private fun setPlayingUi(playing: Boolean) { isPlaying = playing binding.tvPlayLabel.setText(if (playing) R.string.play_ctrl_pause else R.string.play_ctrl_play) diff --git a/example/src/main/java/com/demo/SellyCloudSDK/live/LivePlayForegroundService.kt b/example/src/main/java/com/demo/SellyCloudSDK/live/LivePlayForegroundService.kt new file mode 100644 index 0000000..53a64ac --- /dev/null +++ b/example/src/main/java/com/demo/SellyCloudSDK/live/LivePlayForegroundService.kt @@ -0,0 +1,85 @@ +package com.demo.SellyCloudSDK.live + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.Service +import android.content.Context +import android.content.Intent +import android.os.Build +import androidx.core.app.NotificationCompat +import androidx.core.app.ServiceCompat +import androidx.core.content.ContextCompat +import com.demo.SellyCloudSDK.R + +/** + * 前台播放服务:用于在后台持续拉流并避免系统回收。 + * 仅负责展示常驻通知,不绑定播放业务。 + */ +class LivePlayForegroundService : Service() { + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + ServiceCompat.startForeground( + this, + NOTIFICATION_ID, + buildNotification(), + android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK + ) + } else { + startForeground(NOTIFICATION_ID, buildNotification()) + } + return START_STICKY + } + + override fun onBind(intent: Intent?) = null + + override fun onDestroy() { + try { + stopForeground(STOP_FOREGROUND_REMOVE) + } catch (_: Exception) { + } + super.onDestroy() + } + + private fun buildNotification(): Notification { + ensureChannel() + return NotificationCompat.Builder(this, CHANNEL_ID) + .setContentTitle(getString(R.string.live_play_foreground_title)) + .setContentText(getString(R.string.live_play_foreground_text)) + .setSmallIcon(android.R.drawable.ic_media_play) + .setOngoing(true) + .setOnlyAlertOnce(true) + .build() + } + + private fun ensureChannel() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val manager = getSystemService(NotificationManager::class.java) ?: return + val existing = manager.getNotificationChannel(CHANNEL_ID) + if (existing == null) { + val channel = NotificationChannel( + CHANNEL_ID, + "Live Playback", + NotificationManager.IMPORTANCE_LOW + ) + manager.createNotificationChannel(channel) + } + } + } + + companion object { + private const val CHANNEL_ID = "live_play_foreground" + private const val NOTIFICATION_ID = 0x201 + + fun start(context: Context) { + val intent = Intent(context, LivePlayForegroundService::class.java) + ContextCompat.startForegroundService(context, intent) + } + + fun stop(context: Context) { + val intent = Intent(context, LivePlayForegroundService::class.java) + context.stopService(intent) + } + } +} diff --git a/example/src/main/res/values/strings.xml b/example/src/main/res/values/strings.xml index 90b639d..8f7a198 100644 --- a/example/src/main/res/values/strings.xml +++ b/example/src/main/res/values/strings.xml @@ -97,6 +97,8 @@ 取消静音 截图 画中画 + 正在播放 + 直播播放保持中 直播数据