estoy usando Service
Class en el sistema operativo Android O.
Planeo usar el Service
en el fondo.
La documentación de Android establece que
Si su aplicación está dirigida a un nivel de API 26 o superior, el sistema impone restricciones sobre el uso o la creación de servicios en segundo plano a menos que la aplicación esté en primer plano. Si una aplicación necesita crear un servicio en primer plano, la aplicación debe llamar
startForegroundService()
.
Si lo usa startForegroundService()
, Service
arroja el siguiente error.
Context.startForegroundService() did not then call
Service.startForeground()
¿Qué tiene de malo esto?
android
service
operating-system
foreground
android-8.0-oreo
Buen chico
fuente
fuente
Respuestas:
De los documentos de Google sobre los cambios de comportamiento de Android 8.0 :
Solución: Llamada
startForeground()
enonCreate()
elService
que se utilizaContext.startForegroundService()
Consulte también: Límites de ejecución en segundo plano para Android 8.0 (Oreo)
fuente
onStartCommand
método, pero todavía recibo este error. LlaméstartForegroundService(intent)
a miMainActivity
. Tal vez el servicio se inicia demasiado lento. Creo que el límite de cinco segundos no debería existir antes de que puedan prometer que el servicio se iniciará de inmediato.onStartCommand()
lugar de hacerloonCreate
, porque si cierras el servicio y lo vuelves a iniciar, podría funcionaronStartCommand()
sin llamaronCreate
...Llamé
ContextCompat.startForegroundService(this, intent)
para comenzar el servicio y luegoEn servicio
onCreate
fuente
onCreate
en 5 segundos. Entonces deberían rediseñarlo antes de obligarnos a seguir las reglas.StopService
después de llamarstartForegroundService
.El motivo por el que ocurre este problema es porque el marco de Android no puede garantizar que su servicio se inicie en 5 segundos, pero por otro lado, el marco tiene un límite estricto en la notificación en primer plano, debe activarse en 5 segundos, sin verificar si el marco había intentado iniciar el servicio .
Este es definitivamente un problema de marco, pero no todos los desarrolladores que enfrentan este problema están haciendo su mejor esfuerzo:
startForeground
una notificación debe estar en ambosonCreate
yonStartCommand
, porque si su servicio ya está creado y de alguna manera su actividad está tratando de iniciarlo nuevamente,onCreate
no se llamará.el ID de notificación no debe ser 0; de lo contrario, se producirá el mismo bloqueo, aunque no sea la misma razón.
stopSelf
No debe ser llamado antesstartForeground
.Con todo lo anterior 3, este problema se puede reducir un poco, pero aún no es una solución, la solución real o, digamos, una solución alternativa es degradar su versión de SDK de destino a 25.
Y tenga en cuenta que lo más probable es que Android P siga teniendo este problema porque Google se niega incluso a comprender lo que está sucediendo y no cree que sea su culpa, lea los números 36 y 56 para obtener más información.
fuente
startForeground
. Cambiarlo a 1 me soluciona el problema (con suerte)Lo sé, ya se han publicado demasiadas respuestas, sin embargo, la verdad es que startForegroundService no se puede solucionar a nivel de aplicación y debes dejar de usarlo. Esa recomendación de Google de utilizar la API Service # startForeground () dentro de los 5 segundos posteriores a la llamada al Contexto # startForegroundService () no es algo que una aplicación siempre pueda hacer.
Android ejecuta muchos procesos simultáneamente y no hay ninguna garantía de que Looper llame a su servicio de destino que supuestamente llamará a startForeground () en 5 segundos. Si su servicio objetivo no recibió la llamada en 5 segundos, no tiene suerte y sus usuarios experimentarán una situación de ANR. En su seguimiento de pila verá algo como esto:
Según tengo entendido, Looper ha analizado la cola aquí, encontró un "abusador" y simplemente lo mató. El sistema es feliz y saludable ahora, mientras que los desarrolladores y usuarios no lo son, pero dado que Google limita sus responsabilidades con el sistema, ¿por qué deberían preocuparse por los dos últimos? Aparentemente no lo hacen. ¿Podrían hacerlo mejor? Por supuesto, por ejemplo, podrían haber servido el cuadro de diálogo "La aplicación está ocupada", pidiéndole al usuario que tome una decisión sobre esperar o matar la aplicación, pero por qué molestarse, no es su responsabilidad. Lo principal es que el sistema está sano ahora.
Según mis observaciones, esto ocurre relativamente raramente, en mi caso aproximadamente 1 bloqueo en un mes para usuarios de 1K. Reproducirlo es imposible, e incluso si se reproduce, no hay nada que pueda hacer para arreglarlo permanentemente.
Hubo una buena sugerencia en este hilo para usar "bind" en lugar de "start" y luego, cuando el servicio esté listo, procese onServiceConnected, pero de nuevo, significa no usar las llamadas startForegroundService.
Creo que la acción correcta y honesta del lado de Google sería decirle a todos que startForegourndServcie tiene una deficiencia y no debe usarse.
La pregunta sigue siendo: ¿qué usar en su lugar? Afortunadamente para nosotros, hay JobScheduler y JobService ahora, que son una mejor alternativa para los servicios en primer plano. Es una mejor opción, por eso:
Significa que ya no necesita preocuparse por el manejo de wakelocks y es por eso que no es diferente de los servicios en primer plano. Desde el punto de vista de la implementación, JobScheduler no es su servicio, es un sistema, presumiblemente manejará la cola correctamente y Google nunca terminará su propio hijo :)
Samsung ha cambiado de startForegroundService a JobScheduler y JobService en su Samsung Accessory Protocol (SAP). Es muy útil cuando dispositivos como los relojes inteligentes necesitan hablar con hosts como teléfonos, donde el trabajo necesita interactuar con un usuario a través del hilo principal de una aplicación. Dado que el planificador publica los trabajos en el subproceso principal, es posible. Sin embargo, debe recordar que el trabajo se está ejecutando en el hilo principal y descargar todas las cosas pesadas a otros hilos y tareas asíncronas.
El único inconveniente de cambiar a JobScheduler / JobService es que necesitará refactorizar el código antiguo, y no es divertido. Pasé los últimos dos días haciendo exactamente eso para usar la nueva implementación de SAP de Samsung. Veré mis informes de fallas y te haré saber si vuelves a ver las fallas. Teóricamente no debería suceder, pero siempre hay detalles de los que quizás no estemos al tanto.
ACTUALIZACIÓN No más bloqueos reportados por Play Store. Significa que JobScheduler / JobService no tiene ese problema y cambiar a este modelo es el enfoque correcto para deshacerse del problema startForegroundService de una vez y para siempre. Espero que Google / Android lo lea y eventualmente comente / aconseje / brinde una guía oficial para todos.
ACTUALIZACIÓN 2
Para aquellos que usan SAP y preguntan cómo SAP V2 utiliza JobService, la explicación a continuación.
En su código personalizado, deberá inicializar SAP (es Kotlin):
Ahora necesita descompilar el código de Samsung para ver lo que sucede dentro. En SAAgentV2, observe la implementación de requestAgent y la siguiente línea:
Vaya a la clase SAAdapter ahora y encuentre la función onServiceConnectionRequested que programa un trabajo mediante la siguiente llamada:
SAJobService es solo una implementación de Android'd JobService y esta es la que hace una programación de trabajo:
Como puede ver, la última línea aquí usa JobScheduler de Android'd para obtener este servicio del sistema y programar un trabajo.
En la llamada requestAgent hemos pasado mAgentCallback, que es una función de devolución de llamada que recibirá el control cuando ocurra un evento importante. Así es como se define la devolución de llamada en mi aplicación:
MessageJobs aquí es una clase que he implementado para procesar todas las solicitudes provenientes de un reloj inteligente Samsung. No es el código completo, solo un esqueleto:
Como puede ver, MessageJobs también requiere la clase MessageSocket que necesitaría implementar y que procesa todos los mensajes que provienen de su dispositivo.
En pocas palabras, no es tan simple y requiere un poco de excavación en las partes internas y la codificación, pero funciona, y lo más importante: no se bloquea.
fuente
JobIntentService
ejecuta inmediatamente como unIntentService
Oreo por debajo, pero programa un trabajo en Oreo y por encima, por loJobIntentService
que no comienza de inmediato. Más informaciónJob...
lleva tiempo comenzar y es una versión avanzadaAlarmManager
.Su aplicación se bloqueará si llama
Context.startForegroundService(...)
y luego llamaContext.stopService(...)
antes de queService.startForeground(...)
se llame.Tengo una clara reproducción aquí ForegroundServiceAPI26
He abierto un error en esto en: rastreador de problemas de Google
Se han abierto y cerrado varios errores en esto.
Espero que la mía con pasos claros de reproducción haga el corte.
Información proporcionada por el equipo de google
Rastreador de problemas de Google Comentario 36
Esto no es un error de marco; Es intencional. Si la aplicación inicia una instancia de servicio con
startForegroundService()
, debe pasar esa instancia de servicio al estado de primer plano y mostrar la notificación. Si la instancia de servicio se detiene antes destartForeground()
que se invoque, esa promesa no se cumple: esto es un error en la aplicación.Re # 31 , publicar un Servicio que otras aplicaciones pueden iniciar directamente es fundamentalmente inseguro. Puede mitigarlo un poco tratando todas las acciones de inicio de ese servicio como requeridas
startForeground()
, aunque obviamente eso puede no ser lo que tenía en mente.Rastreador de problemas de Google Comentario 56
Hay un par de escenarios diferentes que conducen al mismo resultado aquí.
El problema semántico absoluto, es que es simplemente un error comenzar algo
startForegroundService()
pero descuidar la transición al primer plano a través destartForeground()
, es solo eso: un problema semántico. Eso se trata como un error de la aplicación, intencionalmente. Detener el servicio antes de pasarlo a primer plano es un error de la aplicación. Ese fue el quid de la OP, y es por eso que este problema se ha marcado como "funcionando según lo previsto".Sin embargo, también hay preguntas sobre la detección espuria de este problema. Eso se está tratando como un problema genuino, aunque se está rastreando por separado de este problema particular del rastreador de errores. No estamos sordos a la queja.
fuente
He investigado sobre esto durante un par de días y obtuve la solución. Ahora en Android O puede establecer la limitación de fondo como se muestra a continuación
El servicio que llama a una clase de servicio.
y la clase de servicio debería ser como
fuente
Tengo un widget que realiza actualizaciones relativamente frecuentes cuando el dispositivo está despierto y vi miles de bloqueos en solo unos días.
El desencadenante del problema
Incluso noté el problema incluso en mi Pixel 3 XL cuando no hubiera pensado que el dispositivo tuviera mucha carga. Y todos y cada uno de los caminos de código estaban cubiertos
startForeground()
. Pero luego me di cuenta de que en muchos casos mi servicio hace el trabajo realmente rápido.Creo que el desencadenante de mi aplicación fue que el servicio estaba terminando antes de que el sistema realmente mostrara una notificación.La solución / solución
Pude deshacerme de todos los accidentes. Lo que hice fue quitar la llamada a
stopSelf()
. (Estaba pensando en retrasar la parada hasta estar bastante seguro de que se mostró la notificación, pero no quiero que el usuario vea la notificación si no es necesario). Cuando el servicio ha estado inactivo durante un minuto o el sistema lo destruye normalmente sin lanzar ninguna excepción.fuente
Solo un aviso porque perdí demasiadas horas en esto. Seguí recibiendo esta excepción a pesar de que estaba llamando
startForeground(..)
como lo primeroonCreate(..)
. Al final descubrí que el problema fue causado por el usoNOTIFICATION_ID = 0
. Usar cualquier otro valor parece arreglar esto.fuente
Tengo una solución para este problema. He verificado esta solución en mi propia aplicación (300K + DAU), que puede reducir al menos el 95% de este tipo de bloqueo, pero aún no puede evitar este problema al 100%.
Este problema ocurre incluso cuando se asegura de llamar a startForeground () justo después de que se inició el servicio como Google documentó. Puede deberse a que el proceso de creación e inicialización del servicio ya cuesta más de 5 segundos en muchos escenarios, y no importa cuándo y dónde llame al método startForeground (), este bloqueo es inevitable.
Mi solución es asegurar que startForeground () se ejecute dentro de los 5 segundos posteriores al método startForegroundService (), sin importar cuánto tiempo deba crearse e inicializarse su servicio. Aquí está la solución detallada.
No use startForegroundService en primer lugar, use bindService () con el indicador auto_create. Esperará la inicialización del servicio. Aquí está el código, mi servicio de muestra es MusicService:
Entonces aquí está la implementación de MusicBinder:
La parte más importante, la implementación de MusicService, el método forceForeground () asegurará que se llame al método startForeground () justo después de startForegroundService ():
Si desea ejecutar el fragmento de código del paso 1 en un intento pendiente, como si desea iniciar un servicio en primer plano en un widget (un clic en el botón del widget) sin abrir su aplicación, puede envolver el fragmento de código en un receptor de difusión , y dispara un evento de difusión en lugar del comando de inicio de servicio.
Eso es todo. Espero eso ayude. Buena suerte.
fuente
Este error también ocurre en Android 8+ cuando se llama a Service.startForeground (int id, Notificación de notificación) mientras el id se establece en 0.
fuente
Como todos los que visitan aquí están sufriendo lo mismo, quiero compartir mi solución que nadie más ha probado antes (de todos modos, en esta pregunta). Les puedo asegurar que está funcionando, incluso en un punto de interrupción detenido. que confirma este método.
El problema es llamar
Service.startForeground(id, notification)
desde el servicio en sí, ¿verdad? Lamentablemente, Android Framework no garantiza llamarService.startForeground(id, notification)
dentroService.onCreate()
de 5 segundos, pero arroja la excepción de todos modos, así que se me ocurrió de esta manera.Context.startForegroundService()
Context.startForegroundService()
desde la conexión de servicio e inmediatamente llameService.startForeground()
dentro de la conexión de servicio.Context.bindService()
método dentro de un try-catch porque en algunas ocasiones la llamada puede arrojar una excepción, en cuyo caso debe confiar en llamarContext.startForegroundService()
directamente y esperar que no falle. Un ejemplo puede ser un contexto de receptor de difusión, sin embargo, obtener el contexto de la aplicación no arroja una excepción en ese caso, pero usar el contexto directamente lo hace.Esto incluso funciona cuando estoy esperando un punto de interrupción después de vincular el servicio y antes de activar la llamada "startForeground". Esperar entre 3-4 segundos no activa la excepción mientras que después de 5 segundos arroja la excepción. (Si el dispositivo no puede ejecutar dos líneas de código en 5 segundos, entonces es hora de tirarlo a la basura).
Entonces, comience creando una conexión de servicio.
Dentro de su servicio, cree una carpeta que devuelva la instancia de su servicio.
Luego, intente vincular el servicio desde ese contexto. Si tiene éxito, llamará al
ServiceConnection.onServiceConnected()
método desde la conexión de servicio que está utilizando. Luego, maneje la lógica en el código que se muestra arriba. Un código de ejemplo se vería así:Parece estar funcionando en las aplicaciones que desarrollo, por lo que debería funcionar cuando lo intentas también.
fuente
Problema con Android O API 26
Si detiene el servicio de inmediato (por lo que su servicio realmente no se ejecuta realmente (redacción / comprensión) y está muy por debajo del intervalo ANR, aún debe llamar a startForeground antes de detenerse a sí mismo
https://plus.google.com/116630648530850689477/posts/L2rn4T6SAJ5
Intenté este enfoque pero aún crea un error:
Estoy usando esto hasta que se resuelva el error
fuente
Estoy enfrentando el mismo problema y después de pasar tiempo encontrando una solución, puede probar el siguiente código. Si está usando
Service
, coloque este código en onCreate, de lo contrario, useIntent Service
este código en onHandleIntent.fuente
He estado investigando este problema y esto es lo que he descubierto hasta ahora. Este bloqueo podría ocurrir si tenemos un código similar a este:
MyForegroundService.java
MainActivity.java
La excepción se produce en el siguiente bloque del código:
ActiveServices.java
Este método se ejecuta antes
onCreate()
deMyForegroundService
porque los horarios de Android de la creación del servicio en el controlador principal hilo, perobringDownServiceLocked
se llama en unaBinderThread
, la cual es una condición de carrera. Significa queMyForegroundService
no tuve la oportunidad de llamar,startForeground
lo que causará el bloqueo.Para solucionar esto, debemos asegurarnos de que
bringDownServiceLocked
no se llame antesonCreate()
deMyForegroundService
.Mediante el uso de emisiones persistentes nos aseguramos de que la emisión no se pierde y
stopReceiver
recibe la parada intención tan pronto como se haya registrado en laonCreate()
deMyForegroundService
. En este momento ya hemos llamadostartForeground(...)
. También tenemos que eliminar esa transmisión fija para evitar que StopReceiver sea notificado la próxima vez.Tenga en cuenta que el método
sendStickyBroadcast
está en desuso y lo uso solo como una solución temporal para solucionar este problema.fuente
Tantas respuestas pero ninguna funcionó en mi caso.
He comenzado un servicio como este.
Y en mi servicio en onStartCommand
Y no olvide establecer NOTIFICATION_ID diferente de cero
Entonces, todo fue perfecto, pero aún se bloqueó en 8.1, por lo que la causa fue la siguiente.
He llamado a detener el primer plano con la notificación de eliminación, pero una vez que el servicio de notificación eliminada se convierte en segundo plano y el servicio en segundo plano no se puede ejecutar en Android O desde segundo plano. comenzó después del empuje recibido.
Así que la palabra mágica es
Hasta ahora, por cualquier motivo que su servicio se esté bloqueando, siga todos los pasos anteriores y disfrute.
fuente
startForeground
ser llamadoonCreate
?https://developer.android.com/reference/android/content/Context.html#startForegroundService(android.content.Intent)
asegúrese de llamar al
Service.startForeground(int, android.app.Notification)
onCreate () para asegurarse de que se llamará ... si tiene alguna condición que le impida hacerlo, será mejor que use el normalContext.startService(Intent)
y llame alService.startForeground(int, android.app.Notification)
usted mismo.Parece que
Context.startForegroundService()
agrega un perro guardián para asegurarte de que llamasteService.startForeground(int, android.app.Notification)
antes de que fuera destruido ...fuente
No llame a ningún StartForgroundServices dentro del método onCreate () , debe llamar a los servicios StartForground en onStartCommand () después de hacer el subproceso de trabajo, de lo contrario obtendrá ANR siempre, así que no escriba inicio de sesión complejo en el subproceso principal de onStartCommand () ;
EDITAR: ¡Precaución! La función startForeground no puede tomar 0 como primer argumento, ¡generará una excepción! este ejemplo contiene una llamada de función incorrecta, cambie 0 a su propia constante que no podría ser 0 o mayor que Max (Int32)
fuente
Incluso después de llamar al
startForeground
enService
, Se estrella en algunos dispositivos si llamamosstopService
justo antes deonCreate
que se llama. Entonces, solucioné este problema al iniciar el servicio con un indicador adicional:y agregó un control en onStartCommand para ver si se comenzó a detener:
PD: si el servicio no se estuviera ejecutando, lo iniciaría primero, lo que es una sobrecarga.
fuente
Debe agregar un permiso como se indica a continuación para el dispositivo Android 9 cuando use el sdk 28 de destino o posterior o la excepción siempre sucederá:
fuente
Solo verifico el
PendingIntent
nulo o no antes de llamar alcontext.startForegroundService(service_intent)
función.esto funciona para mi
fuente
simplemente llame al método startForeground inmediatamente después de crear el servicio o IntentService. Me gusta esto:
fuente
Ok, algo que noté en esto que podría ayudar a algunos otros también. Esto es estrictamente de las pruebas para ver si puedo descubrir cómo solucionar los sucesos que estoy viendo. Por simplicidad, digamos que tengo un método que llama a esto desde el presentador.
Esto se bloqueará con el mismo error. El Servicio NO comenzará hasta que se complete el método, por lo tanto, no
onCreate()
en el servicio.Por lo tanto, incluso si actualiza la interfaz de usuario del hilo principal, SI tiene algo que pueda retrasar ese método después, no comenzará a tiempo y le dará el temido Error de primer plano. En mi caso, estábamos cargando algunas cosas en una cola y cada una llamó
startForegroundService
, pero había algo de lógica involucrada con cada una en el fondo. Entonces, si la lógica tardó demasiado en terminar ese método, ya que se llamaron consecutivamente, el tiempo de bloqueo. El viejostartService
simplemente lo ignoró y siguió su camino y como lo llamábamos cada vez, la siguiente ronda terminaría.Esto me dejó preguntándome, si llamé al servicio desde un hilo en segundo plano, ¿no podría estar completamente vinculado al inicio y ejecutarse de inmediato, así que comencé a experimentar? Aunque esto NO lo inicia de inmediato, no se bloquea.
No voy a pretender saber por qué no se bloquea, aunque sospecho que esto lo obliga a esperar hasta que el hilo principal pueda manejarlo de manera oportuna. Sé que no es ideal vincularlo al hilo principal, pero dado que mi uso lo llama en segundo plano, no estoy realmente preocupado si espera hasta que pueda completarse en lugar de bloquearse.
fuente
Estoy agregando un código en la respuesta @humazed. Así que no hay notificación inicial. Puede ser una solución, pero funciona para mí.
Estoy agregando transparentColor en pequeño icono y color en la notificación. Funcionará.
fuente
He solucionado el problema al iniciar el servicio en
startService(intent)
lugar deContext.startForeground()
llamarstartForegound()
inmediatamente despuéssuper.OnCreate()
. Además, si inicia el servicio en el arranque, puede iniciar la Actividad que inicia el servicio en la transmisión de arranque. Aunque no es una solución permanente, funciona.fuente
Actualización de datos en
onStartCommand(...)
onBind (...)
onBind(...)
es un mejor evento de ciclo de vida para iniciarstartForeground
vs.onCreate(...)
porqueonBind(...)
pasa en unIntent
que puede contener datos importantesBundle
necesarios para inicializarService
. Sin embargo, no es necesario ya queonStartCommand(...)
se llama cuandoService
se crea por primera vez o se llama después.onStartCommand (...)
startForeground
enonStartCommand(...)
es importante para actualizar elService
vez que ya ha sido creado.Cuando
ContextCompat.startForegroundService(...)
se llama después de queService
se ha creado unonBind(...)
yonCreate(...)
no se llama. Por lo tanto, los datos actualizados se pueden pasar aonStartCommand(...)
través deIntent
Bundle
para actualizar los datos en elService
.Muestra
Estoy usando este patrón para implementar el
PlayerNotificationManager
en el Coinverse aplicación de noticias criptomoneda.Activity / Fragment.kt
AudioService.kt
fuente
Servicio
Ensayador
Resultado
fuente