Entendido. Eu já passei pelo inferno e voltei com esse problema. Veja como proceder. Existem bugs. Esta postagem descreve como analisar bugs na implementação e contornar os problemas.
Para resumir, é assim que as coisas devem funcionar. Os serviços em execução serão eliminados rotineiramente e encerrados a cada 30 minutos ou mais. Os serviços que desejam permanecer ativos por mais tempo devem chamar Service.startForeground, que coloca uma notificação na barra de notificação, para que os usuários saibam que seu serviço está funcionando permanentemente e potencialmente consumindo bateria. Apenas 3 processos de serviço podem se autodenominar como serviços de primeiro plano a qualquer momento. Se houver mais de três serviços em primeiro plano, o Android nomeará o serviço mais antigo como candidato para eliminação e encerramento.
Infelizmente, existem bugs no Android com relação à priorização de serviços de primeiro plano, que são acionados por várias combinações de sinalizadores de vinculação de serviço. Mesmo que você tenha nomeado corretamente seu serviço como um serviço de primeiro plano, o Android pode encerrar seu serviço de qualquer maneira, se alguma conexão aos serviços em seu processo tiver sido feita com certas combinações de sinalizadores de ligação. Os detalhes são fornecidos abaixo.
Observe que muito poucos serviços precisam ser serviços de primeiro plano. Geralmente, você só precisa ser um serviço de primeiro plano se tiver uma conexão à Internet constantemente ativa ou de longa duração de algum tipo que possa ser ligada e desligada ou cancelada pelos usuários. Exemplos de serviços que precisam do status de primeiro plano: servidores UPNP, downloads de longa duração de arquivos muito grandes, sincronização de sistemas de arquivos por wi-fi e reprodução de música.
Se você está apenas pesquisando ocasionalmente, ou esperando em receptores de transmissão do sistema ou eventos do sistema, seria melhor ativar seu serviço em um cronômetro, ou em resposta a receptores de transmissão, e então deixar seu serviço morrer quando concluído. Esse é o comportamento projetado para serviços. Se você simplesmente precisa permanecer vivo, continue lendo.
Depois de marcar as caixas em requisitos conhecidos (por exemplo, chamar Service.startForeground), o próximo lugar a observar é nos sinalizadores que você usa nas chamadas Context.bindService. Os sinalizadores usados para vincular afetam a prioridade do processo do serviço de destino de várias maneiras inesperadas. Mais especificamente, o uso de certos sinalizadores de ligação pode fazer com que o Android faça downgrade incorretamente de seu serviço de primeiro plano para um serviço regular. O código usado para atribuir a prioridade do processo foi agitado fortemente. Notavelmente, há revisões na API 14+ que podem causar bugs ao usar sinalizadores de ligação mais antigos; e existem bugs definitivos em 4.2.1.
Seu amigo em tudo isso é o utilitário sysdump, que pode ser usado para descobrir qual prioridade o gerente de atividades atribuiu ao seu processo de serviço e identificar casos em que atribuiu uma prioridade incorreta. Coloque seu serviço em funcionamento e emita o seguinte comando em um prompt de comando em seu computador host:
processos de atividade do adb shell dumpsys> tmp.txt
Use o bloco de notas (não o wordpad / escrita) para examinar o conteúdo.
Primeiro, verifique se você conseguiu executar o serviço em primeiro plano. A primeira seção do arquivo dumpsys contém uma descrição das propriedades ActivityManager para cada processo. Procure uma linha como a seguinte que corresponda ao seu aplicativo na primeira seção do arquivo dumpsys:
APLICATIVO UID 10068 ProcessRecord {41937d40 2205: tunein.service / u0a10068}
Verifique se foregroundServices = true na seção a seguir. Não se preocupe com as configurações ocultas e vazias; eles descrevem o estado das Atividades no processo e não parecem ser particularmente relevantes para processos com serviços neles. Se foregroundService não for verdadeiro, você precisará chamar Service.startForeground para torná-lo verdadeiro.
A próxima coisa que você precisa examinar é a seção próxima ao final do arquivo intitulada "Lista de LRU do processo (classificada por oom_adj):". As entradas nesta lista permitem que você determine se o Android realmente classificou seu aplicativo como um serviço de primeiro plano. Se o seu processo está no final desta lista, é um candidato principal para o extermínio sumário. Se o seu processo estiver próximo ao topo da lista, ele é virtualmente indestrutível.
Vejamos uma linha nesta tabela:
Proc
Este é um exemplo de serviço de primeiro plano que fez tudo certo. O campo-chave aqui é o campo "adj =". Isso indica a prioridade que seu processo foi atribuído pelo ActivityManagerService depois que tudo foi dito feito. Você deseja que seja "adj = prcp" (serviço visível em primeiro plano); ou "adj = vis" (processo visível com uma atividade) ou "anterior" (processo com uma atividade em primeiro plano). Se for "adj = svc" (processo de serviço), ou "adj = svcb" (serviço legado?), Ou "adj = bak" (processo em segundo plano vazio), então seu processo é um provável candidato a encerramento e será encerrado não menos frequentemente do que a cada 30 minutos, mesmo se não houver qualquer pressão para recuperar a memória. Os sinalizadores restantes na linha são principalmente informações de diagnóstico de depuração para engenheiros do Google. As decisões de rescisão são feitas com base nos campos adj. Resumidamente, / FS indica um serviço de primeiro plano; / FA indica um processo em primeiro plano com uma atividade. / B indica um serviço em segundo plano. O rótulo no final indica a regra geral sob a qual o processo recebeu uma prioridade. Normalmente, deve corresponder ao campo adj =; mas o valor adj = pode ser ajustado para cima ou para baixo em alguns casos devido a sinalizadores de ligação em ligações ativas com outros serviços ou atividades.
Se você tropeçou em um bug com sinalizadores de ligação, a linha dumpsys será semelhante a esta:
Proc
Observe como o valor do campo adj está incorretamente definido como "adj = bak" (processo em segundo plano vazio), o que se traduz aproximadamente em "por favor, termine-me agora para que eu possa encerrar esta existência inútil" para fins de eliminação do processo. Observe também o sinalizador (fg-service) no final da linha, que indica que "regras de serviço forground foram usadas para determinar a configuração" adj ". Apesar do fato de as regras fg-service terem sido usadas, este processo recebeu uma configuração adj "bak", e não viverá por muito tempo. Simplificando, isso é um bug.
Portanto, o objetivo é garantir que seu processo sempre obtenha "adj = prcp" (ou melhor). E o método para atingir esse objetivo é ajustar os sinalizadores de ligação até conseguir evitar bugs na atribuição de prioridade.
Aqui estão os bugs que conheço. (1) Se QUALQUER serviço ou atividade já se vinculou ao serviço usando Context.BIND_ABOVE_CLIENT, você corre o risco de que a configuração adj = seja rebaixada para "bak", mesmo que a vinculação não esteja mais ativa. Isso é particularmente verdadeiro se você também tiver ligações entre serviços. Um bug claro nas fontes 4.2.1. (2) Definitivamente, nunca use BIND_ABOVE_CLIENT para uma vinculação serviço a serviço. Não o use para conexões de atividade para serviço. O sinalizador usado para implementar o comportamento BIND_ABOVE_CLIENT parece ser definido por processo, em vez de por conexão, então ele aciona bugs com ligações serviço a serviço, mesmo se não houver uma atividade ativa a serviço vinculação com o conjunto de sinalizadores. Também parece haver problemas com o estabelecimento de prioridade quando há vários serviços no processo, com ligações de serviço a serviço. Usando Context.BIND_WAIVE_PRIORITY (API 14) em ligações serviço a serviço parece ajudar. Context.BIND_IMPORTANT parece ser uma ideia mais ou menos boa ao vincular de uma Activity a um serviço. Fazer isso aumenta a prioridade do processo um degrau acima quando a atividade está em primeiro plano, sem causar nenhum dano aparente quando a atividade é pausada ou concluída.
Mas, no geral, a estratégia é ajustar seus sinalizadores de bindService até que sysdump indique que seu processo recebeu a prioridade correta.
Para meus propósitos, usando Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT para ligações de atividade para serviço e Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY para ligações serviço a serviço parece fazer a coisa certa. Sua milhagem pode ser diferente.
Meu aplicativo é bastante complexo: dois serviços de segundo plano, cada um dos quais pode conter independentemente estados de serviço de primeiro plano, mais um terceiro que também pode assumir o estado de serviço de primeiro plano; dois dos serviços se ligam condicionalmente; o terceiro liga-se ao primeiro, sempre. Além disso, as atividades são executadas em um processo separado (torna a animação mais suave). Executar as Atividades e Serviços no mesmo processo não pareceu fazer diferença.
A implementação das regras para processos de eliminação (e o código-fonte usado para gerar o conteúdo dos arquivos sysdump) podem ser encontrados no arquivo android principal
frameworks\base\services\java\com\android\server\am\ActivityManagerService.java.
Boa chance.
PS: Esta é a interpretação das strings sysdump para Android 5.0. Não trabalhei com eles, então faça deles o que quiser. Eu acredito que você deseja que 4 seja 'A' ou 'S' e 5 seja "IF" ou "IB" e 1 seja o mais baixo possível (provavelmente abaixo de 3, uma vez que apenas 3 processos de serviço em primeiro plano são mantidos ativos na configuração padrão).
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