[英]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 的几个线程,但它们没有帮助。
Use Service instead LifecycleService as parent class.使用 Service 而不是 LifecycleService 作为父 class。
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 } }
Add the following tag to service at mainfest将以下标签添加到 mainfest 的服务中
android:foregroundServiceType="mediaPlayback" android:foregroundServiceType="mediaPlayback"
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.