簡體   English   中英

Activity 銷毀后使用 startForeground 服務不保留

[英]Using startForeground Service not keeping after Activity destroyed

目前,我需要一個綁定(音樂)服務,因為我需要與之交互。 但我也希望它不會停止,即使所有組件都已解除綁定。

我的服務代碼:

class ServicePlayer : LifecycleService() {
            
        var mediaPlayer: MediaPlayer? = null
        var notificationManager: NotificationManager? = null
        var notificationBuilder: NotificationCompat.Builder? = null
        private val mBinder: IBinder = PlayerBinder()
        private val NOTIFICATION_ID = 1111
  
    
        override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
            super.onStartCommand(intent, flags, startId)
            return START_REDELIVER_INTENT
        }
    
        inner class PlayerBinder : Binder() {
            val service: ServicePlayer
                get() = this@ServicePlayer
        }
    
        override fun onBind(intent: Intent): IBinder? {
            super.onBind(intent)
            return mBinder
        }
    
        override fun onCreate() {
            super.onCreate()
           
            mediaPlayer = MediaPlayer()
            mediaPlayer!!.setOnCompletionListener(this)
            mediaPlayer!!.setOnBufferingUpdateListener(this)
            mediaPlayer!!.setOnErrorListener(this)
            val filter = IntentFilter()
            filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED)
            filter.addAction(Intent.ACTION_SCREEN_ON)
            registerReceiver(receiver, filter)
        }
    
    
        override fun onDestroy() {
    
            super.onDestroy()
            mediaPlayer!!.reset()
            mediaPlayer!!.release()
            Log.i("DESTROY SERVICE", "destroy")
            unregisterReceiver(receiver)
        }
    
    
    
        fun play(trackIndex: Int, tracks: ArrayList<Track>?) {
            
                    ...
                    
                    val intent = Intent(BUFFERING)
                    this@ServicePlayer.sendBroadcast(intent)
             
        }
    
        fun pause() {
                if (mediaPlayer!!.isPlaying) {
                    mediaPlayer!!.pause()
                    PlayerLiveData.isPlaying.value = false
                    val intent = Intent(UPDATE_UI)
                    this@ServicePlayer.sendBroadcast(intent)
                    
                    //Show notification
                    CoroutineScope(Dispatchers.Default).launch {
                        showNotification()
                    }
                }
        }
        
        
        private fun hideNotification() {
            notificationManager!!.cancel(NOTIFICATION_ID)
            stopForeground(true)
        }
        
        
        private fun showNotification() {
            notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

                ...
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                val CHANNEL_ID = "controls_channel_id"
                val CHANNEL_NAME = "Play tracks"
                val channel = NotificationChannel(CHANNEL_ID,CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW)
                ...
                
                val mMediaSession = MediaSessionCompat(applicationContext, getString(R.string.app_name))
                mMediaSession.setFlags(
                        MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
                                MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
                )
                notificationManager!!.createNotificationChannel(channel)
                notificationBuilder = NotificationCompat.Builder(applicationContext)
                        .setChannelId(CHANNEL_ID)
                        .setContentText(artistText)
                        .setContentTitle(track.title)
                       
                       ...
            } else {
                notificationBuilder = NotificationCompat.Builder(applicationContext)

                ...

                notificationBuilder!!
                        .setSmallIcon(R.drawable.ic_notification)
                        .setContentIntent(contentIntent)
                        .setCustomContentView(remoteSmallViews)
                        .setCustomBigContentView(remoteViews)
            }
    
    
            CoroutineScope(Dispatchers.Default).launch {
                val notification = notificationBuilder!!.build()
                startForeground(NOTIFICATION_ID, notification)
                val notificationTarget = NotificationTarget(
                        applicationContext
                        , R.id.imgThumb, remoteViews
                        , notification, NOTIFICATION_ID
                )
                ...
                    lifecycleScope.launch {
                        val request = ImageRequest.Builder(applicationContext)
                                .data(thumb)
                                .error(R.drawable.placeholder_song)
                                .placeholder(R.drawable.placeholder_song)
                                .build()
                        val drawable = imageLoader.execute(request).drawable
                        val bitmap = (drawable as BitmapDrawable).bitmap
    
    
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        
                        
                            notificationBuilder!!.setLargeIcon(bitmap)
                            val notification = notificationBuilder!!.build()
                            notificationManager!!.notify(NOTIFICATION_ID,notification)
                            
                            
                            //Start Foreground service
                            startForeground(NOTIFICATION_ID, notification)
                            
                            
                        } 
                    }
            }
    
        }      
    }

清單文件聲明:

<service android:name=".services.ServicePlayer" android:enabled="true" android:exported="true"/>

在活動中使用服務

class MainActivity : AppCompatActivity() {

    lateinit var binding: MainActivityBinding

    private lateinit var audioPlayerService: ServicePlayer
    
     override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val intent = Intent(this, ServicePlayer::class.java)
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)

        binding = DataBindingUtil.setContentView(this, R.layout.main_activity)
        binding.lifecycleOwner = this
        binding.viewmodel = mainViewModel
    }
    
    
       private val serviceConnection: ServiceConnection = object : ServiceConnection {
        override fun onServiceDisconnected(name: ComponentName) {
            // audioPlayerService = null;
        }

        override fun onServiceConnected(name: ComponentName, service: IBinder) {
            audioPlayerService = (service as ServicePlayer.PlayerBinder).service
            if (audioPlayerService.trackIndex !== -1) {
                //updatePlaybackUI()
            }
        }
    }
    
}

即使在活動被破壞后,我怎樣才能讓我的服務在后台運行。 我參考了 StackOverflow 的幾個線程,但它們沒有幫助。

  1. 使用 Service 而不是 LifecycleService 作為父 class。

  2. 分別為 onCreate 和 onDestroy 方法添加部分喚醒鎖開始和停止調用。

     private val powerManager get() = (this.getSystemService(Context.POWER_SERVICE) as PowerManager) private var wakeLock: PowerManager.WakeLock? = null private fun startWakeLock() { if (wakeLock == null) { wakeLock = powerManager.newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, "${packageName}:wakeLock" ) wakeLock?.acquire() } } private fun stopWakeLock() { if (wakeLock?.isHeld == true) { wakeLock?.release() wakeLock = null } }
  3. 將以下標簽添加到 mainfest 的服務中

android:foregroundServiceType="mediaPlayback"

  1. 您應該將服務作為活動的前台啟動

一旦每個客戶端解除綁定,綁定服務就會停止,並且當客戶端(您的Activity )被銷毀時會自動發生

如果您的應用程序銷毀客戶端時您的客戶端仍綁定到服務,則銷毀會導致客戶端解除綁定。 更好的做法是在客戶端完成與服務的交互后立即解除綁定。 這樣做允許空閑服務關閉。

如果您希望服務繼續運行, Started Service將執行此操作。 您仍然可以綁定到它,但它不會停止,直到您明確告訴它停止並且沒有綁定客戶端。


不過老實說,如果您正在制作某種媒體播放器,您可能會想要使用MediaBrowserServiceCompat框架。 它允許您創建一個與MediaBrowser配合使用的服務(除其他外,它執行綁定),並允許您使用MediaSession獲取帶有控件的媒體通知等。

關於這些東西的一些鏈接:

如果您不關心這些,那么startService / startForegroundService (或ContextCompat#startForegroundService )將為您提供一個剛剛運行的服務,但這些鏈接可能會給您一些關於東西的指針

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM