Okey dokey. He pasado por el infierno y he vuelto a este problema. He aquí cómo proceder. Hay errores. Esta publicación describe cómo analizar errores en la implementación y solucionar problemas.
Solo para resumir, así es como se supone que funcionan las cosas. Los servicios en ejecución se eliminarán de forma rutinaria y finalizarán cada 30 minutos aproximadamente. Los servicios que deseen permanecer activos durante más tiempo deben llamar a Service.startForeground, que coloca una notificación en la barra de notificaciones, para que los usuarios sepan que su servicio está funcionando permanentemente y potencialmente está consumiendo la vida útil de la batería. Solo 3 procesos de servicio pueden nominarse a sí mismos como servicios de primer plano en un momento dado. Si hay más de tres servicios en primer plano, Android nominará el servicio más antiguo como candidato para eliminación y terminación.
Desafortunadamente, hay errores en Android con respecto a la priorización de los servicios en primer plano, que se desencadenan por varias combinaciones de indicadores de enlace de servicios. Aunque haya designado correctamente su servicio como un servicio en primer plano, Android puede terminar su servicio de todos modos, si alguna vez se ha realizado alguna conexión a los servicios en su proceso con ciertas combinaciones de indicadores vinculantes. Los detalles se dan a continuación.
Tenga en cuenta que muy pocos servicios deben ser servicios de primer plano. En general, solo necesita ser un servicio en primer plano si tiene una conexión a Internet constantemente activa o de larga duración de algún tipo que los usuarios puedan activar y desactivar, o cancelar. Ejemplos de servicios que necesitan un estado de primer plano: servidores UPNP, descargas de larga duración de archivos muy grandes, sincronización de sistemas de archivos por wi-fi y reproducción de música.
Si solo está sondeando ocasionalmente, o esperando a los receptores de transmisión del sistema o eventos del sistema, sería mejor que activara su servicio con un temporizador o en respuesta a los receptores de transmisión, y luego deje que su servicio se apague una vez que se complete. Ese es el comportamiento diseñado para los servicios. Si simplemente debes seguir con vida, sigue leyendo.
Después de marcar las casillas de los requisitos bien conocidos (por ejemplo, llamar a Service.startForeground), el siguiente lugar para mirar son las banderas que usa en las llamadas de Context.bindService. Los indicadores utilizados para enlazar afectan la prioridad del proceso de servicio de destino de diversas formas inesperadas. En particular, el uso de ciertos indicadores de enlace puede hacer que Android degrade incorrectamente su servicio de primer plano a un servicio regular. El código utilizado para asignar la prioridad del proceso se ha batido bastante. En particular, hay revisiones en API 14+ que pueden causar errores al usar indicadores de enlace más antiguos; y hay errores definidos en 4.2.1.
Su amigo en todo esto es la utilidad sysdump, que puede usarse para averiguar qué prioridad le ha asignado el administrador de actividades a su proceso de servicio y detectar casos en los que ha asignado una prioridad incorrecta. Ponga en marcha su servicio y luego emita el siguiente comando desde un símbolo del sistema en su computadora host:
adb shell procesos de actividad dumpsys> tmp.txt
Utilice el bloc de notas (no el bloc de notas / escritura) para examinar el contenido.
Primero verifique que ha logrado ejecutar con éxito su servicio en el estado de primer plano. La primera sección del archivo dumpsys contiene una descripción de las propiedades de ActivityManager para cada proceso. Busque una línea como la siguiente que corresponda a su aplicación en la primera sección del archivo dumpsys:
APP UID 10068 ProcessRecord {41937d40 2205: tunein.service / u0a10068}
Verifique que foregroundServices = true en la siguiente sección. No se preocupe por los ajustes ocultos y vacíos; describen el estado de las actividades en el proceso y no parecen ser particularmente relevantes para procesos con servicios en ellos. Si foregroundService no es verdadero, debe llamar a Service.startForeground para que sea verdadero.
Lo siguiente que debe mirar es la sección cerca del final del archivo titulado "Lista de proceso LRU (ordenada por oom_adj):". Las entradas en esta lista le permiten determinar si Android realmente ha clasificado su aplicación como un servicio de primer plano. Si su proceso está al final de esta lista, es un candidato principal para el exterminio sumario. Si su proceso está cerca de la parte superior de la lista, es prácticamente indestructible.
Veamos una línea en esta tabla:
Proc
Este es un ejemplo de un servicio de primer plano que ha hecho todo bien. El campo clave aquí es el campo "adj =". Eso indica la prioridad que el ActivityManagerService asignó a su proceso después de que todo se dijo hecho. Quiere que sea "adj = prcp" (servicio de primer plano visible); o "adj = vis" (proceso visible con una actividad) o "fore" (proceso con una actividad en primer plano). Si se trata de "adj = svc" (proceso de servicio), "adj = svcb" (¿servicio heredado?) O "adj = bak" (proceso de fondo vacío), entonces su proceso es un candidato probable para la terminación y se cancelará. no menos de cada 30 minutos, incluso si no hay ninguna presión para recuperar la memoria. Las banderas restantes en la línea son en su mayoría información de depuración de diagnóstico para los ingenieros de Google. Las decisiones sobre la terminación se toman en función de los campos adj. Brevemente, / FS indica un servicio en primer plano; / FA indica un proceso en primer plano con una actividad. / B indica un servicio en segundo plano. La etiqueta al final indica la regla general según la cual se asignó prioridad al proceso. Por lo general, debe coincidir con el campo adj =; pero el valor adj = se puede ajustar hacia arriba o hacia abajo en algunos casos debido a las banderas de enlace en enlaces activos con otros servicios o actividades.
Si ha tropezado con un error con banderas vinculantes, la línea dumpsys se verá así:
Proc
Observe cómo el valor del campo adj está configurado incorrectamente en "adj = bak" (proceso de fondo vacío), que se traduce aproximadamente como "por favor, ciérreme ahora para que pueda terminar con esta existencia sin sentido" para fines de búsqueda de procesos. También tenga en cuenta la marca (fg-service) al final de la línea que indica que "las reglas de servicio en tierra se usaron para determinar la configuración" adj ". A pesar de que se usaron las reglas fg-service, a este proceso se le asignó una configuración adj. "bak", y no vivirá por mucho tiempo En pocas palabras, esto es un error.
Por lo tanto, el objetivo es garantizar que su proceso siempre obtenga "adj = prcp" (o mejor). Y el método para lograr ese objetivo es modificar las banderas de enlace hasta que logre evitar errores en la asignación de prioridad.
Estos son los errores que conozco. (1) Si CUALQUIER servicio o actividad se ha vinculado al servicio utilizando Context.BIND_ABOVE_CLIENT, corre el riesgo de que la configuración adj = se reduzca a "bak" incluso si esa vinculación ya no está activa. Esto es particularmente cierto si también tiene enlaces entre servicios. Un error claro en las fuentes 4.2.1. (2) Definitivamente nunca use BIND_ABOVE_CLIENT para un enlace de servicio a servicio. Tampoco lo use para conexiones de actividad a servicio. La bandera utilizada para implementar el comportamiento de BIND_ABOVE_CLIENT parece estar configurada por proceso, en lugar de por conexión, por lo que desencadena errores con enlaces de servicio a servicio incluso si no hay una actividad activa a servicio vinculante con la bandera puesta. También parece haber problemas para establecer la prioridad cuando hay varios servicios en el proceso, con enlaces de servicio a servicio. El uso de Context.BIND_WAIVE_PRIORITY (API 14) en enlaces de servicio a servicio parece ayudar. Context.BIND_IMPORTANT parece ser una idea más o menos buena cuando se vincula desde una actividad a un servicio. Al hacerlo, aumenta la prioridad de su proceso un nivel más alto cuando la Actividad está en primer plano, sin causar ningún daño aparente cuando la Actividad se detiene o finaliza.
Pero en general, la estrategia es ajustar sus indicadores bindService hasta que sysdump indique que su proceso ha recibido la prioridad correcta.
Para mis propósitos, usando Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT para enlaces de actividad a servicio y Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY para enlaces de servicio a servicio parece hacer lo correcto. Su kilometraje puede diferir.
Mi aplicación es bastante compleja: dos servicios en segundo plano, cada uno de los cuales puede tener estados de servicio en primer plano de forma independiente, más un tercero que también puede tomar el estado de servicio en primer plano; dos de los servicios se vinculan entre sí de forma condicional; el tercero se une al primero, siempre. Además, las actividades se ejecutan en un proceso separado (hace que la animación sea más fluida). Ejecutar las actividades y los servicios en el mismo proceso no pareció marcar ninguna diferencia.
La implementación de las reglas para los procesos de barrido (y el código fuente utilizado para generar el contenido de los archivos sysdump) se puede encontrar en el archivo principal de Android
frameworks\base\services\java\com\android\server\am\ActivityManagerService.java.
Bon chance.
PD: Aquí está la interpretación de las cadenas de sysdump para Android 5.0. No he trabajado con ellos, así que haz de ellos lo que quieras. Creo que desea que 4 sea 'A' o 'S', y 5 sea "IF" o "IB", y 1 sea lo más bajo posible (probablemente por debajo de 3, ya que solo 3 tres procesos de servicio en primer plano se mantienen activos en la configuración predeterminada).
Example:
Proc # : prcp F/S/IF trm: 0 31719: neirotech.cerebrum.attention:blePrcs/u0a77 (fg-service)
Format:
Proc # {1}: {2} {3}/{4}/{5} trm: {6} {7}: {8}/{9} ({10}
1: Order in list: lower is less likely to get trimmed.
2: Not sure.
3:
B: Process.THREAD_GROUP_BG_NONINTERACTIVE
F: Process.THREAD_GROUP_DEFAULT
4:
A: Foreground Activity
S: Foreground Service
' ': Other.
5:
-1: procState = "N ";
ActivityManager.PROCESS_STATE_PERSISTENT: procState = "P ";
ActivityManager.PROCESS_STATE_PERSISTENT_UI:procState = "PU";
ActivityManager.PROCESS_STATE_TOP: procState = "T ";
ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND: procState = "IF";
ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND: procState = "IB";
ActivityManager.PROCESS_STATE_BACKUP:procState = "BU";
ActivityManager.PROCESS_STATE_HEAVY_WEIGHT: procState = "HW";
ActivityManager.PROCESS_STATE_SERVICE: procState = "S ";
ActivityManager.PROCESS_STATE_RECEIVER: procState = "R ";
ActivityManager.PROCESS_STATE_HOME: procState = "HO";
ActivityManager.PROCESS_STATE_LAST_ACTIVITY: procState = "LA";
ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: procState = "CA";
ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: procState = "Ca";
ActivityManager.PROCESS_STATE_CACHED_EMPTY: procState = "CE";
{6}: trimMemoryLevel
{8} Process ID.
{9} process name
{10} appUid