简体   繁体   English

Activity 销毁后使用 startForeground 服务不保留

[英]Using startForeground Service not keeping after Activity destroyed

Currently, I need a bound (Music)Service, because I need to interact with it.目前,我需要一个绑定(音乐)服务,因为我需要与之交互。 But I also want it to not be stopped, even when all components have unbound themselves.但我也希望它不会停止,即使所有组件都已解除绑定。

My service code:我的服务代码:

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)
                            
                            
                        } 
                    }
            }
    
        }      
    }

Manifest file declaration:清单文件声明:

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

Using service in activity在活动中使用服务

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()
            }
        }
    }
    
}

How can I keep my service running in background even after activity destroyed.即使在活动被破坏后,我怎样才能让我的服务在后台运行。 I refer few threads of StackOverflow but they are not helpful.我参考了 StackOverflow 的几个线程,但它们没有帮助。

  1. Use Service instead LifecycleService as parent class.使用 Service 而不是 LifecycleService 作为父 class。

  2. Add partial wake lock start and stop calls to onCreate and onDestroy methods respectively.分别为 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. Add the following tag to service at mainfest将以下标签添加到 mainfest 的服务中

android:foregroundServiceType="mediaPlayback" android:foregroundServiceType="mediaPlayback"

  1. You should start service as foreground from the activity您应该将服务作为活动的前台启动

A bound service stops once every client unbinds from it, and that happens automatically when the client (your Activity ) is destroyed一旦每个客户端解除绑定,绑定服务就会停止,并且当客户端(您的Activity )被销毁时会自动发生

If your client is still bound to a service when your app destroys the client, destruction causes the client to unbind.如果您的应用程序销毁客户端时您的客户端仍绑定到服务,则销毁会导致客户端解除绑定。 It is better practice to unbind the client as soon as it is done interacting with the service.更好的做法是在客户端完成与服务的交互后立即解除绑定。 Doing so allows the idle service to shut down.这样做允许空闲服务关闭。

If you want the service to just keep running, a Started Service will do that.如果您希望服务继续运行, Started Service将执行此操作。 You can still bind to it, but it won't stop until you explicitly tell it to stop and there are no clients bound.您仍然可以绑定到它,但它不会停止,直到您明确告诉它停止并且没有绑定客户端。


Honestly though, if you're making some kind of media player, you'll probably want to use the MediaBrowserServiceCompat framework.不过老实说,如果您正在制作某种媒体播放器,您可能会想要使用MediaBrowserServiceCompat框架。 It allows you to create a service that plays nice with MediaBrowser (which does the binding, among other things) and lets you use a MediaSession to get a media notification with controls and all that.它允许您创建一个与MediaBrowser配合使用的服务(除其他外,它执行绑定),并允许您使用MediaSession获取带有控件的媒体通知等。

A few links about that stuff:关于这些东西的一些链接:

If you don't care about any of that then startService / startForegroundService (or ContextCompat#startForegroundService ) will get you a service that just runs, but those links might give you some pointers about stuff如果您不关心这些,那么startService / startForegroundService (或ContextCompat#startForegroundService )将为您提供一个刚刚运行的服务,但这些链接可能会给您一些关于东西的指针

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM