我有一个已启动且有界的服务(音乐播放器),并且我想在单击通知的关闭按钮时停止它。但是,如果有活动绑定到该服务,该服务将不会停止。 单击按钮后如何停止服务?
我尝试收集服务中的活动列表并调用对它们的回调以解除绑定(实际上是finish()
,然后在onStop()
上将调用unbind()
),然后我停止了该服务通过调用stopSelf()
来实现,但我认为这不是一个好主意,因为生命周期问题和某些活动被多次添加并维护列表非常困难。应该有更好的方法!尽管搜索了几个小时之后我什么都没找到。
这是我的PlayerService
的{{1}}
onStartCommand()
和我的活动:
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
info("Starting Player Service ...")
started=true
if (intent == null) {
error("intent was null")
pause()
return START_STICKY
}
if (intent.hasExtra(KEY_SONGS)) {
if(intent.hasExtra(KEY_PLAYLIST_ID)) playlistId=intent.getStringExtra(KEY_PLAYLIST_ID)
initExoPlayer(intent)
} else {
info("No new song information found for intent, intent's action: ${intent.action}")
}
if (intent.action == null) intent.action = ""
when (intent.action) {
ACTION_PLAY -> if (isPlaying()) pause() else play()
ACTION_CANCEL -> {
//TODO: implement a better and the correct way
for (client in clients) {
client.onStop()
}
stopSelf()
}
else -> showNotification()
}
return START_STICKY
}
这是我的自定义通知:
playerService.clients.add(object : PlayerService.Client {
override fun onStop() {
finish()
}
})
答案 0 :(得分:1)
尝试类似的操作
fun stopServices() {
val serviceIntent = Intent(this, NotificationService::class.java)
serviceIntent.action = Constants.ACTION.STOPTFOREGROUND_ACTION
stopService(serviceIntent)
}
在您的活动中调用此方法
在您的服务启动命令中
ACTION.STOPFOREGROUND_ACTION -> {
//Toast.makeText(this, "Service Stoped", Toast.LENGTH_SHORT).show();
stopForeground(true);
stopSelf();
Intent intents = new Intent("com.app.package.ACTION_STOP");
LocalBroadcastManager.getInstance(this).sendBroadcast(intents);
//startActivity(new Intent(this,MainActivity.class));
}
val notificationLayout= new RemoteViews(getPackageName(), R.layout.custom_layout);
Intent closeIntent = new Intent(this, NotificationService.class);
closeIntent.setAction(Constants.ACTION.STOPFOREGROUND_ACTION);
PendingIntent cancelPendingIntent= PendingIntent.getService(this, 0, closeIntent, 0);
关闭按钮单击
notificationLayout.setOnClickPendingIntent(R.id.btn_cancel, cancelPendingIntent);
答案 1 :(得分:1)
如果此任务似乎令人沮丧,则可能是因为这是一个不寻常的用例,并且在某种程度上超出了Android设计人员的想法。您可能要问自己,是否还有一种类似于Android的方式来实现目标(即无需枚举绑定的客户)。
考虑您的要求:
Activity
)我认为您可能已选择通过绑定到Service
开始播放,并通过onDestroy()
结束播放来结束播放。这是设计错误,它将引起您很多问题。毕竟,根据docs,本地的进程内Service
实际上只是表示“应用程序希望在不与用户交互的情况下执行长时间运行的操作”,并且“表示自己可以在主线程上完成工作。”
相反,通过在播放开始时使用Activity
,在播放停止时使用onStart()
,将每个onStop()
约束在startService()
和stopSelf()
之间,您将自动完成目标1、2和4。当应用程序处于所有Activities
不在屏幕上的状态时,它将有0个绑定的客户端。除此之外,如果没有startService()
尚未解决,则它是OS立即终止的候选对象。在任何其他状态下,操作系统都会保留它。
这没有任何多余的逻辑来跟踪绑定的Activities
。当然,您可能想在播放器播放时startForeground()
,完成时stopForeground()
(大概已经处理了该细节)。
剩下的问题是如何实现目标3。要通过Service
或通知在Activities
中进行状态更改,您可以只使用Intents
(就像您已经使用过的一样)。在这种情况下,Service
将通过stopSelf()
管理自己的生命周期。再次假设您在正确的时间(Oreo +)使用startForeground()
,否则Android可能会由于background execution limits而终止您的应用。
实际上,如果您选择使用Intents
,您可能会发现根本不需要将Activities
绑定到Service
(IOW,目标#1在这种情况下是无关紧要的)。如果您需要直接引用该服务(通过Service
),则只需绑定到Binder
。