通过解除绑定所有绑定的活动来停止服务

时间:2019-02-02 10:13:13

标签: android android-service android-notifications android-service-binding

我有一个已启动且有界的服务(音乐播放器),并且我想在单击通知的关闭按钮时停止它。但是,如果有活动绑定到该服务,该服务将不会停止。 单击按钮后如何停止服务?

我尝试收集服务中的活动列表并调用对它们的回调以解除绑定(实际上是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()
        }
    })

2 个答案:

答案 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的方式来实现目标(即无需枚举绑定的客户)。

考虑您的要求:

  1. 当用户与应用互动时(通过 Activity
  2. 当用户在后台运行应用程序时,播放器应继续存在
  3. 当用户点击通知时,播放器应停止
  4. 用户完成使用后,应清除该应用

我认为您可能已选择通过绑定到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