使用startForeground Service在Activity销毁后不保留

时间:2021-03-10 17:00:13

标签: android kotlin android-service android-service-binding

目前,我需要一个绑定(音乐)服务,因为我需要与之交互。但我也希望它不会被停止,即使所有组件都已解除绑定。

我的服务代码:

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 的几个线程,但它们没有帮助。

2 个答案:

答案 0 :(得分:0)

  1. 使用 Service 而不是 LifecycleService 作为父类。

  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 作为前台启动服务

答案 1 :(得分:0)

一旦每个客户端解除绑定,绑定服务就会停止,并且 that happens automatically 当客户端(您的 Activity)被销毁时

<块引用>

如果您的应用程序销毁客户端时您的客户端仍绑定到服务,则销毁会导致客户端解除绑定。最好在与客户端交互完成后立即解除绑定客户端服务。这样做可以关闭空闲服务。

如果您希望服务继续运行,Started Service 可以做到这一点。您仍然可以绑定到它,但它不会停止,除非您明确告诉它停止并且没有绑定客户端。


老实说,如果您正在制作某种媒体播放器,您可能需要使用 MediaBrowserServiceCompat 框架。它允许您创建一个与 MediaBrowser(它进行绑定等)配合良好的服务,并允许您使用 MediaSession 来获取带有控件的媒体通知等等。

关于这些东西的一些链接:

如果您不关心其中任何一个,那么 startService/startForegroundService(或 ContextCompat#startForegroundService)将为您提供一个刚刚运行的服务,但这些链接可能会给您一些提示关于东西