Остановите службу, отменив все связанные действия - PullRequest
0 голосов
/ 02 февраля 2019

У меня есть запущенный и ограниченный сервис (музыкальный проигрыватель), и я хочу остановить его, когда нажата кнопка закрытия уведомления.однако, служба не будет остановлена, если есть действия, связанные с ней.Как я могу остановить обслуживание, когда кнопка нажата?

Я попытался собрать список действий в обслуживании и вызвать обратный вызов для их отмены (на самом деле finish(), который затем на onStop() вызовет unbind()) и затем я останавливаю службу, вызывая stopSelf(), но я не думаю, что это хорошая идея, потому что проблемы жизненного цикла и некоторые действия, добавляемые несколько раз и ведение списка, трудны.Должен быть лучший способ!хотя я ничего не нашел после поиска часов.

вот мои PlayerService 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()
        }
    })

А вот и мое пользовательское уведомление:

private fun getCustomNotification(name: String, showMainActivity: Intent): Notification {
    /*
        some codes to prepare notificationLayout
     */
    notificationLayout.setTextViewText(R.id.tv_name, name)
    notificationLayout.setImageViewResource(R.id.btn_play, playDrawableID)
    notificationLayout.setOnClickPendingIntent(R.id.btn_play, playPendingIntent)
    notificationLayout.setOnClickPendingIntent(R.id.btn_cancel, cancelPendingIntent)

    // Apply the layouts to the notification
    return NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.drawable.logo_mouj)
            .setCustomContentView(notificationLayout)
            .setContentIntent(PendingIntent.getActivity(this, 0, showMainActivity, 0))
            .setOngoing(true)
            .build()

}

Ответы [ 2 ]

0 голосов
/ 02 февраля 2019

Если эта задача кажется разочаровывающей, возможно, это связано с тем, что это необычный вариант использования и несколько выходит за рамки того, что задумывали дизайнеры Android.Вы можете спросить себя, существует ли более «Android-подобный» способ достижения ваших целей (т. Е. без необходимости перечислять связанных клиентов).

Рассмотрим ваши требования:

  1. Плеер должен быть доступен, пока пользователь взаимодействует с приложением (через Activity)
  2. Плеер должен продолжать существовать, пока приложение пользователя находится в фоновом режиме
  3. Плеер должен остановиться, когда пользователь нажмет на уведомление
  4. Приложение должно быть очищено, когда пользователь покончит с ним

Я думаю, что вы, возможно, выбралиначать воспроизведение с привязки к Service и завершить воспроизведение с помощью onDestroy().Это ошибка проектирования, и она вызовет у вас много проблем.В конце концов, согласно docs , локальный внутрипроцессный Service действительно просто представляет «желание приложения выполнить более продолжительную операцию, не взаимодействуя с пользователем», и оно «не являетсяозначает работу над основным потоком. "

Вместо этого, удерживая каждый Activity в пределах между onStart() и onStop(), используя startService(), когда начинается воспроизведение, и stopSelf() когда игра останавливается, вы автоматически достигнете целей 1, 2 и 4. Когда приложение находится в состоянии, когда все Activities находятся за пределами экрана, у него будет 0 связанных клиентов.Если, кроме того, startService() не выдаются, это является кандидатом на немедленное завершение ОС.В любом другом состоянии ОС будет поддерживать его.

Это не требует какой-либо дополнительной причудливой логики для отслеживания границы Activities.Конечно, вы, вероятно, захотите startForeground(), когда игрок играет, и stopForeground(), когда это будет сделано (вероятно, вы уже обработали эту деталь).

Это оставляет вопрос о том, как выполнитьЦель № 3.Чтобы изменить состояние в Service из Activities или из уведомления, вы можете просто использовать Intents (так же, как вы уже).В этом случае Service будет управлять своим жизненным циклом через stopSelf().Это предполагает, опять же, что вы используете startForeground() в нужное время (Oreo +), иначе Android может завершить работу вашего приложения из-за пределов фонового выполнения .

Фактически,если вы решите использовать Intents, вы можете обнаружить, что вам даже не нужно связывать свой Activities с Service (IOW, цель № 1 в данном случае не имеет значения).Вам нужно только связать с Service, если вам нужна прямая ссылка на эту услугу (через Binder).

0 голосов
/ 02 февраля 2019

Попробуйте что-то подобное

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);
...