Context.startForegroundService () затем не вызывал Service.startForeground () - все еще проблема

Проверив проблему с Google и множество других проблем с SO, я пришел к решению, которое добавил внизу.

Я забочусь о следующем:

  • в onCreate И в onStartCommand я обязательно перемещаю службу на передний план (если она еще не на переднем плане)
  • Я не просто останавливаю службу, я делаю это с помощью дополнительной команды, которую отправляю обработчику onStartCommand, чтобы убедиться, что служба не останавливается во время запуска (в конечном итоге до того, как она сможет завершить переход на передний план)
  • Я никогда не останавливаю службу напрямую (context.stopService(...)), я всегда останавливаю службу с помощью команды из самой запущенной службы - поэтому я убеждаюсь, что ее можно остановить только тогда, когда она работает на переднем плане, а не во время запуска
  • служба может быть остановлена ​​только один раз
  • Я останавливаю службу, отменяя ее уведомление на переднем плане, и только после этого останавливаю саму службу.

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

Полученный журнал

2019-07-29 21:41:27,146 [[BaseOverlayService:62 onCreate]]: onCreate
2019-07-29 21:41:27,146 [[BaseOverlayService:142 b]]: BEFORE moveToForeground (called by onCreate)
2019-07-29 21:41:27,152 [[BaseOverlayService:159 b]]: AFTER moveToForeground (called by onCreate) - moved to foreground: true
2019-07-29 21:41:27,176 [[BaseOverlayService:79 onStartCommand]]: onStartCommand: isForeground: true | action: null | isStopping: false
2019-07-29 21:41:27,945 [[BaseOverlayService:142 b]]: BEFORE moveToForeground (called by updateNotification [OverlayService [onInitFinished]])
2019-07-29 21:41:27,947 [[BaseOverlayService:159 b]]: AFTER moveToForeground (called by updateNotification [OverlayService [onInitFinished]]) - moved to foreground: false

Это журнал одного отчета о сбоях - как вы можете видеть, служба перемещена на передний план в строке 3 (moved to foreground: true), а в строке 6 она знает, что она уже запущена на переднем плане.

Я активно использую это приложение на своем устройстве Android 9 (24/7, оно постоянно работает) и у меня нет проблем, и, поскольку я использую базовый класс снизу, проблема действительно минимизировалась до нескольких сбоев в месяц в целом. Тем не менее, приведенный выше журнал показывает, что моя служба работает на переднем плане в течение миллисекунд, но может дать сбой, как показано ниже:

android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{86fa711 u0 com.my.app/com.my.app.service.OverlayService}
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1855)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:6986)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1445)

Кто-нибудь видит проблемы с моим базовым классом?

Код

abstract class BaseOverlayService<T : BaseOverlayService<T>>(
        val foregroundNotificationId: Int,
        val notificationCreator: ((service: T) -> Notification)
) : Service() {

    companion object {

        val DEBUG = true

        // helper function, simply checks if this service is already running by checking the ActivityManager
        inline fun <reified T : BaseOverlayService<T>> isRunning(context: Context): Boolean {
            return Tools.isServiceRunning(context, T::class.java)
        }

        inline fun <reified T : BaseOverlayService<T>> start(context: Context, checkIfServiceIsRunning: Boolean) {
            if (checkIfServiceIsRunning && isRunning<T>(context)) {
                L.logIf { DEBUG }?.d { "IGNORED start intent" }
                return
            }

            L.logIf { DEBUG }?.d { "send start intent" }
            val intent = Intent(context, T::class.java)
            ContextCompat.startForegroundService(context, intent)
        }

        inline fun <reified T : BaseOverlayService<T>> sendAction(context: Context, checkIfServiceIsRunning: Boolean, action: String, intentUpdater: ((Intent) -> Unit) = {}) {
            if (checkIfServiceIsRunning && !isRunning<T>(context)) {
                L.logIf { DEBUG }?.d { "IGNORED action intent - action: $action" }
                return
            }

            L.logIf { DEBUG }?.d { "send action intent - action: $action" }
            val intent = Intent(context, T::class.java)
            intent.action = action
            intentUpdater(intent)
            ContextCompat.startForegroundService(context, intent)
        }
    }

    protected var isForeground = false
        private set
    protected var isStopping: Boolean = false
        private set

    // ------------------------
    // service events
    // ------------------------

    final override fun onCreate() {

        L.logIf { DEBUG }?.d { "onCreate" }

        super.onCreate()

        if (foregroundNotificationId <= 0) {
            throw RuntimeException("foregroundNotificationId must be > 0!")
        }

        moveToForeground("onCreate")

        onCreateEvent()
    }

    final override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

        val returnValue = START_STICKY

        L.logIf { DEBUG }?.d { "onStartCommand: isForeground: $isForeground | action: ${intent?.action} | isStopping: $isStopping" }

        // 1) if service is stopping, we ignore the event
        if (isStopping) {
            return returnValue
        }

        // 2) if service is not running in foreground we make it run in the foreground
        if (!isForeground) {
            moveToForeground("onStartCommand")
        }

        onStartCommandEvent(intent, flags, startId)

        return returnValue
    }

    final override fun onBind(intent: Intent): IBinder? {
        // overlay service is never bound!
        return null
    }

    // ------------------------
    // Forwarded abstract events
    // ------------------------

    abstract fun onCreateEvent()

    abstract fun onStartCommandEvent(intent: Intent?, flags: Int, startId: Int)

    abstract fun onStopEvent()

    // ------------------------
    // protected functions
    // ------------------------

    protected fun stopService() {

        L.logIf { DEBUG }?.d { "stopService | isStopping: $isStopping" }

        if (isStopping) {
            L.logIf { DEBUG }?.d { "IGNORED stopService" }
            return
        }

        onStopEvent()
        isStopping = true
        moveToBackground(true)
        stopSelf()

        L.logIf { DEBUG }?.d { "stopService finished" }
    }

    protected fun updateNotification(caller: String) {
        moveToForeground("updateNotification [$caller]")
    }

    // ------------------------
    // private foreground/background functions
    // ------------------------

    private fun moveToForeground(caller: String): Boolean {

        L.logIf { DEBUG }?.d { "BEFORE moveToForeground (called by $caller)" }

        // 1) Create notification
        val notification = notificationCreator(this as T)

        // 2.1) Create foreground notification
        val result = if (!isForeground) {
            isForeground = true
            startForeground(foregroundNotificationId, notification)
            true
        }
        // 2.2) Update foreground notification
        else {
            notificationManager.notify(foregroundNotificationId, notification)
            false
        }

        L.logIf { DEBUG }?.d { "AFTER moveToForeground (called by $caller) - moved to foreground: $result" }

        return result
    }

    private fun moveToBackground(cancelNotification: Boolean) {
        isForeground = false
        super.stopForeground(cancelNotification)
    }

    // ------------------------
    // private helper functions
    // ------------------------

    private val notificationManager by lazy {
        getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    }
}

person prom85    schedule 30.07.2019    source источник
comment
Если вы нашли решение своего вопроса, опубликуйте его как ответ и примите его.   -  person matdev    schedule 30.07.2019
comment
@matdev попробуйте решение из моего вопроса, это лучшее, что я мог найти на данный момент, и работает большую часть времени. Если я решу это за пределами SO, я отправлю ответ   -  person prom85    schedule 31.07.2019
comment
Похоже, это всего лишь еще один пример серьезной ошибки, активно вводимой Google в качестве меры безопасности, и их полного игнорирования жалоб со стороны разработчиков. Я пытался решить эту проблему целую вечность и в итоге получил более 20 различных отчетов о сбоях, каждый из которых обычно срабатывает от 3 до 10 раз в месяц, и все они содержат эту основную ошибку. [вставить это безумие !, безумие? это ГООООГЛ! GIF здесь]   -  person 0101100101    schedule 09.10.2019


Ответы (1)


У меня была такая же проблема, и я мог решить ее так:

В Android 9 Pie, если ваша служба не вызывает startForeground в течение 5 секунд после ее запуска с помощью команды startForegroundService ..., она выдает ошибку ANR +.

Решение состоит в том, чтобы добавить startForeground () с вашим уведомлением прямо в начале метода onStartCommand вашей службы переднего плана, например:

final override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int 
{
    startForeground(YOUR_FOREGROUND_NOTIFICATION_ID, Notification);
    // rest of your code here...
}
person JBA    schedule 17.09.2019
comment
Если вы ответите на мой вопрос, это уже часть моего решения - person prom85; 18.09.2019
comment
Извините, вы действительно правы. Вы можете посмотреть, может ли случиться так, что условие 2) в вашем коде не будет выполнено, а затем moveToForeground () не будет вызван ... и в конце система убьет вашу службу? - person JBA; 18.09.2019
comment
Где в документации указано правило 5 секунд? - person IgorGanapolsky; 03.06.2020
comment
Это написано здесь, проверьте помеченную звездочкой синюю фоновую заметку, которая заканчивается: После создания службы она должна вызвать свой метод startForeground () в течение пяти секунд. - person JBA; 04.06.2020