Как сохранить службу даже после уничтожения активности?

Здесь я запускаю сервис для воспроизведения музыки. Этот фрагмент кода находится в моем методе onStart() моей деятельности

if(musicServiceStartIntent == null) {
            musicServiceStartIntent = new Intent(this, MusicService.class);
            startService(musicServiceStartIntent);
            bindService(musicServiceStartIntent, musicConnection, Context.BIND_AUTO_CREATE);
        } 

Сначала я запускаю свой сервис, а затем привязываю его. И я вызываю unbindservice() в методе onDestroy(). Моя активность была уничтожена, и служба остановилась.

unbindService(musicConnection);

Объявление файла манифеста

<service android:name=".Services.MusicService"/>

Как я могу поддерживать работу службы в фоновом режиме даже после уничтожения активности. Я ссылаюсь на несколько потоков StackOverflow, но они бесполезны.


person saa    schedule 01.08.2016    source источник
comment
stackoverflow.com/questions/22895872/ Попробуйте это. Это сработало для меня   -  person Jay Ghosh    schedule 01.08.2016
comment
не отвязывайте службу на Ondestroy(), а затем также уничтожайте, а затем делайте службу липкой.   -  person Aditi    schedule 01.08.2016
comment
я попробовал твой ответ. если я не отвязываю свой сервис onDestroy(), он выдает ошибку.   -  person saa    schedule 01.08.2016
comment
@JayGhosh: посмотри на мой фрагмент кода. Я написал это, как вы предложили. Но у меня не работает.... :(   -  person saa    schedule 01.08.2016
comment
Какова цель привязки вашего Сервиса?   -  person IgorGanapolsky    schedule 10.03.2017


Ответы (5)


Используйте свой сервис в startForeground, используя уведомление, вы можете поддерживать свой сервис.

person Tanveer Bulsari    schedule 01.08.2016
comment
это помогает .. ваш ответ не всегда полезен. я хочу запускать эту службу непрерывно, когда моя деятельность выполняется и после ее уничтожения. - person saa; 01.08.2016
comment
чем вы должны запустить свою службу на переднем плане - person Tanveer Bulsari; 01.08.2016
comment
как запустить службу на переднем плане? я звоню как этот startService (намерение); - person saa; 01.08.2016
comment
Ваше предложение работает на меня. Пожалуйста, отредактируйте свой ответ, я отмечу его как правильный ответ. Спасибо д - person saa; 01.08.2016
comment
На устройствах Android с большим объемом оперативной памяти вам не нужно переводить службу на передний план, но это сделает ее более отказоустойчивой. Если служба использует намерения, registerReciever в отдельном ServiceHandler! ServiceHandler предотвратит сбой вашей службы при получении намерения после того, как ваша активность была уничтожена пользователем/системой. Пример такой службы: github.com/mauron85/cordova-plugin-background-geolocation/blob/ - person mauron85; 07.09.2016

Вам просто нужно запустить службу, не привязывая ее к жизненному циклу активности.

Intent intent = new Intent(context, SomeService.class);
startService(intent);

И ваша служба может использовать START_STICKY / START_REDELIVER_INTENT, чтобы убедиться, что ваша служба будет воссоздана, когда система Android уничтожит вашу службу.

@Override
public int onStartCommand(final Intent intent, int flags, int startId) {
   //other code
   return START_STICKY;
}

При необходимости вы можете использовать Service.startForeground(notificationId, уведомление), чтобы убедиться, что ваша служба не будет уничтожена системой.

person HendraWD    schedule 01.08.2016
comment
Вы упомянули startForeground? - person HendraWD; 01.08.2016
comment
Я отправил точно такой же ответ, постарайтесь внимательно посмотреть - person nutella_eater; 01.08.2016
comment
я написал все возможные ответы, посмотри еще раз - person HendraWD; 01.08.2016
comment
на простом примере - person HendraWD; 01.08.2016

См. https://developer.android.com/guide/components/services.html#Foreground. Музыкальный проигрыватель, воспроизводящий музыку из службы, должен работать на переднем плане, поскольку пользователь явно знает о его работе. Уведомление в строке состояния может указывать на текущую песню и позволять пользователю запускать действие для взаимодействия с музыкальным проигрывателем. Чтобы запросить запуск службы на переднем плане, вызовите startForeground().

person anoo_radha    schedule 01.08.2016

Есть три важных приема:

  1. Вызовите startForegroundService, который создает долго работающую службу, не ограниченную привязанным контекстом, и обещает вызвать startForeground позже.
  2. Вернуть START_STICKY в onStartComand
  3. Вызовите startForeground с уведомлением, как было обещано в (1).

Например, если вы хотите запустить TimerService, в своей TimerActivity вы должны сделать:

private var timerService: TimerService? = null

private val timerServiceConnection = object : ServiceConnection {

    override fun onServiceConnected(className: ComponentName, service: IBinder) {
        val binder = service as TimerService.Binder
        timerService = binder.getService()
    }

    override fun onServiceDisconnected(arg0: ComponentName) {
    }
}

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    startButton.setOnClickListener {
        timerService?.startTimer(60L, 0L)
    }
}

override fun onStart() {
    super.onStart()

    Intent(this, TimerService::class.java).also {
        ContextCompat.startForegroundService(this, it) // that's the first trick
        bindService(it, timerServiceConnection, Context.BIND_AUTO_CREATE)
    }
}

Ваш TimerService будет примерно таким:

class TimerService : Service() {

    private val binder = Binder()

    private var serviceLooper: Looper? = null

    private var serviceHandler: ServiceHandler? = null

    private var timer: CountDownTimer? = null

    private val notificationUtil by lazy {
        NotificationUtil(this)
    }

    override fun onCreate() {
        HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND).apply {
            start()
            serviceLooper = looper
            serviceHandler = ServiceHandler(looper)
        }
    }

    override fun onBind(intent: Intent?): IBinder? = binder

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val timerRemaining = intent?.getLongExtra(EXTRA_REMAINING, 0) ?: 0L
        if (timerRemaining != 0L) {
            serviceHandler?.obtainMessage()?.also { msg ->
                msg.arg1 = startId
                msg.data.putLong(EXTRA_REMAINING, timerRemaining)
                serviceHandler?.sendMessage(msg)
            }
        }

        return START_STICKY // that's the second trick
    }

    override fun onDestroy() {
        super.onDestroy()
        timer?.cancel()
    }

    fun startTimer(secondsRemaining: Long, id: Long) {
        Intent(this, TimerService::class.java).apply {
            putExtra(EXTRA_REMAINING, secondsRemaining)
        }.also {
            onStartCommand(it, 0, id.toInt())
        }
    }

    fun stopTimer() {
        timer?.cancel()
    }

    fun updateNotification(secondsRemaining: Long){
        val notification = NotificationCompat.Builder(this, NotificationUtil.CHANNEL_ID_TIMER)
                .setSmallIcon(R.drawable.ic_timer)
                .setAutoCancel(true)
                .setDefaults(0)
                .setContentTitle(secondsRemaining.formatSeconds())
                .setContentText("Timer")
                .setContentIntent(notificationUtil.getPendingIntentWithStack(this, TimerActivity::class.java))
                .setOngoing(true)
                .build()
        startForeground(NotificationUtil.NOTIFICATION_ID, notification) // that's the last trick
    }

    private fun sendMessage(remaining: Long) {
        Intent(TimerService::class.java.simpleName).apply {
            putExtra(EXTRA_REMAINING, remaining)
        }.also {
            LocalBroadcastManager.getInstance(this).sendBroadcast(it)
        }
    }

    private inner class ServiceHandler(looper: Looper) : Handler(looper) {

        override fun handleMessage(msg: Message) {
            val secondsRemaining = msg.data.getLong(EXTRA_REMAINING)
            notificationUtil.showTimerStarted(secondsRemaining)

            timer = object : CountDownTimer(secondsRemaining * 1000, 1000) {

                override fun onTick(millisUntilFinished: Long) {
                    Log.i(this::class.java.simpleName, "tick ${(millisUntilFinished / 1000L).formatSeconds()}")
                    updateNotification(millisUntilFinished / 1000)
                    sendMessage(millisUntilFinished / 1000)
                }

                override fun onFinish() {
                    Log.i(this::class.java.simpleName, "finish")
                    notificationUtil.showTimerEnded()
                    sendMessage(0)
                    stopSelf()
                }
            }.start()
        }
    }

    inner class Binder : android.os.Binder() {
        // Return this instance of LocalService so clients can call public methods
        fun getService(): TimerService = this@TimerService
    }

    companion object {

        const val EXTRA_REMAINING = "EXTRA_REMAINING"
        const val NOTIFICATION_ID = 1 // cannot be 0

        fun Long.formatSeconds(): String {
            val s = this % 60
            val m = this / 60 % 60
            val h = this / (60 * 60) % 24
            return if (h > 0) String.format("%d:%02d:%02d", h, m, s)
            else String.format("%02d:%02d", m, s)
        }
    }

}
person Allan Veloso    schedule 17.09.2019

person    schedule
comment
start_sticky означает, что активность будет воссоздана после того, как она была уничтожена Android. Учтите, что сервис можно убить - person nutella_eater; 01.08.2016
comment
@alexeypolusov START_STICKY не имеет ничего общего с Активностью! - person IgorGanapolsky; 10.03.2017
comment
@IgorGanapolsky Я имел в виду сервис, извините. - person nutella_eater; 13.03.2017
comment
попробуйте это: developer.android.com/guide/components/services.html#Foreground - person Shubham; 14.03.2017