Estou usando a Service
classe no sistema operacional Android.
Eu pretendo usar o Service
em segundo plano.
A documentação do Android afirma que
Se seu aplicativo atingir o nível 26 ou superior da API, o sistema impõe restrições ao uso ou criação de serviços em segundo plano, a menos que o aplicativo em si esteja em primeiro plano. Se um aplicativo precisar criar um serviço em primeiro plano, ele deverá chamar
startForegroundService()
.
Se você usar startForegroundService()
, Service
lança o seguinte erro.
Context.startForegroundService() did not then call
Service.startForeground()
O que há de errado nisso?
android
service
operating-system
foreground
android-8.0-oreo
Cara legal
fonte
fonte
Respostas:
Nos documentos do Google no Android 8.0, o comportamento muda :
Solução: Chamada
startForeground()
emonCreate()
para oService
que você usaContext.startForegroundService()
Veja também: Limites de execução em segundo plano para Android 8.0 (Oreo)
fonte
onStartCommand
método, mas ainda estou recebendo esse erro. Eu ligueistartForegroundService(intent)
no meuMainActivity
. Talvez o serviço seja iniciado muito devagar. Acho que o limite de cinco segundos não deve existir antes que eles possam prometer que o serviço é iniciado imediatamente.onStartCommand()
vez deonCreate
, porque se você fechar o serviço e iniciá-lo novamente, ele pode ir paraonStartCommand()
sem chamaronCreate
...Liguei
ContextCompat.startForegroundService(this, intent)
para iniciar o serviço entãoEm serviço
onCreate
fonte
onCreate
em 5 segundos. Portanto, eles devem redesenhar antes de nos forçar a seguir as regras.StopService
após ligarstartForegroundService
.Por que esse problema está acontecendo é porque a estrutura do Android não pode garantir que seu serviço seja iniciado em 5 segundos, mas, por outro lado, a estrutura possui um limite estrito de notificação em primeiro plano, que deve ser acionada em 5 segundos, sem verificar se a estrutura tentou iniciar o serviço .
Esse é definitivamente um problema de estrutura, mas nem todos os desenvolvedores que enfrentam esse problema estão fazendo o melhor possível:
startForeground
uma notificação deve estar em ambosonCreate
eonStartCommand
, porque se o seu serviço já estiver criado e, de alguma forma, sua atividade estiver tentando iniciá-lo novamente,onCreate
não será chamada.o ID da notificação não deve ser 0, caso contrário, a mesma falha ocorrerá, mesmo que não seja o mesmo motivo.
stopSelf
não deve ser chamado antesstartForeground
.Com todos os itens acima de 3, esse problema pode ser reduzido um pouco, mas ainda não é uma correção, a correção real ou, digamos, a solução alternativa é fazer o downgrade da versão sdk de destino para 25.
E observe que provavelmente o Android P ainda continuará com esse problema porque o Google se recusa a entender o que está acontecendo e não acredita que isso seja culpa deles, leia # 36 e # 56 para obter mais informações
fonte
startForeground
. Mudá-lo para 1 corrige o problema para mim (espero)Eu sei que muitas respostas já foram publicadas, mas a verdade é: startForegroundService não pode ser corrigido no nível do aplicativo e você deve parar de usá-lo. A recomendação do Google de usar a API Service # startForeground () dentro de 5 segundos após a chamada do Context # startForegroundService () não é algo que um aplicativo sempre possa fazer.
O Android executa muitos processos simultaneamente e não há garantia de que o Looper ligue para o serviço de destino que deveria chamar startForeground () dentro de 5 segundos. Se o serviço de destino não recebeu a ligação dentro de 5 segundos, você estará sem sorte e seus usuários enfrentarão a situação de ANR. No rastreamento da pilha, você verá algo assim:
Pelo que entendi, Looper analisou a fila aqui, encontrou um "agressor" e simplesmente a matou. O sistema está feliz e saudável agora, enquanto desenvolvedores e usuários não, mas como o Google limita suas responsabilidades ao sistema, por que eles deveriam se preocupar com os dois últimos? Aparentemente eles não. Eles poderiam melhorar? Obviamente, por exemplo, eles poderiam ter exibido a caixa de diálogo "O aplicativo está ocupado", pedindo ao usuário que tome uma decisão sobre aguardar ou matar o aplicativo, mas por que se preocupar, não é responsabilidade deles. O principal é que o sistema esteja saudável agora.
Pelas minhas observações, isso acontece relativamente raramente, no meu caso, aproximadamente 1 falha em um mês para usuários de 1K. É impossível reproduzi-lo e, mesmo que seja reproduzido, não há nada que você possa fazer para corrigi-lo permanentemente.
Houve uma boa sugestão nesse encadeamento para usar "bind" em vez de "start" e, quando o serviço estiver pronto, processe onServiceConnected, mas, novamente, isso significa não usar as chamadas startForegroundService.
Penso que a ação correta e honesta do lado do Google seria dizer a todos que o startForegourndServcie tem uma deficiência e não deve ser usado.
A questão ainda permanece: o que usar? Felizmente para nós, agora existem JobScheduler e JobService, que são uma alternativa melhor para os serviços em primeiro plano. É uma opção melhor, por causa disso:
Isso significa que você não precisa mais se preocupar em lidar com wakelocks e é por isso que não é diferente dos serviços em primeiro plano. Do ponto de vista da implementação, o JobScheduler não é o seu serviço, é um sistema, presumivelmente ele manipulará a fila corretamente e o Google nunca encerrará seu próprio filho :)
A Samsung mudou de startForegroundService para JobScheduler e JobService em seu Samsung Accessory Protocol (SAP). É muito útil quando dispositivos como smartwatches precisam conversar com hosts como telefones, onde o trabalho precisa interagir com um usuário por meio do thread principal de um aplicativo. Como os trabalhos são publicados pelo planejador no encadeamento principal, torna-se possível. Lembre-se, porém, de que o trabalho está sendo executado no encadeamento principal e descarrega todo o material pesado para outros encadeamentos e tarefas assíncronas.
A única armadilha da mudança para o JobScheduler / JobService é que você precisará refatorar o código antigo, e isso não é divertido. Passei os últimos dois dias fazendo exatamente isso para usar a nova implementação SAP da Samsung. Vou assistir meus relatórios de falhas e informar se você vê as falhas novamente. Teoricamente, isso não deveria acontecer, mas sempre há detalhes dos quais podemos não estar cientes.
ATUALIZAÇÃO Não há mais falhas relatadas pela Play Store. Isso significa que o JobScheduler / JobService não tem esse problema e a mudança para esse modelo é a abordagem correta para se livrar do problema startForegroundService de uma vez por todas. Espero que o Google / Android leia e acabe comentando / aconselhando / fornecendo uma orientação oficial para todos.
ATUALIZAÇÃO 2
Para aqueles que usam o SAP e perguntam como o SAP V2 utiliza a explicação do JobService, veja abaixo.
No seu código personalizado, você precisará inicializar o SAP (é Kotlin):
Agora você precisa descompilar o código da Samsung para ver o que está acontecendo lá dentro. No SAAgentV2, consulte a implementação requestAgent e a seguinte linha:
Vá para a classe SAAdapter agora e encontre a função onServiceConnectionRequested que agenda um trabalho usando a seguinte chamada:
O SAJobService é apenas uma implementação do Android'd JobService e é esse que realiza um agendamento de tarefas:
Como você vê, a última linha aqui usa o JobScheduler do Android para obter esse serviço do sistema e agendar um trabalho.
Na chamada requestAgent, passamos pelo mAgentCallback, que é uma função de retorno de chamada que receberá controle quando um evento importante acontecer. É assim que o retorno de chamada é definido no meu aplicativo:
MessageJobs aqui é uma classe que eu implementei para processar todas as solicitações provenientes de um smartwatch Samsung. Não é o código completo, apenas um esqueleto:
Como você vê, o MessageJobs também requer a classe MessageSocket que você precisaria implementar e processa todas as mensagens vindas do seu dispositivo.
Resumindo, não é tão simples e requer alguma pesquisa interna e codificação, mas funciona e o mais importante - não trava.
fonte
JobIntentService
é executado imediatamente como umIntentService
Oreo abaixo, mas programa um trabalho acima e acima do Oreo, paraJobIntentService
que não seja iniciado imediatamente. Mais informaçõesJob...
leva tempo para começar e é uma versão avançada doAlarmManager
.Seu aplicativo falhará se você ligar
Context.startForegroundService(...)
e depois ligarContext.stopService(...)
antes daService.startForeground(...)
chamada.Tenho uma reprodução clara aqui ForegroundServiceAPI26
Abri um bug no seguinte: Rastreador de problemas do Google
Vários erros foram abertos e fechados. Não será corrigido.
Espero que o meu, com etapas claras de reprodução, faça o corte.
Informações fornecidas pela equipe do Google
Rastreador de problemas do Google Comentário 36
Este não é um bug do framework; é intencional. Se o aplicativo iniciar uma instância de serviço
startForegroundService()
, ele deve fazer a transição para o estado de primeiro plano e mostrar a notificação. Se a instância do serviço for interrompida antes destartForeground()
ser chamada, essa promessa não será cumprida: isso é um bug no aplicativo.Re # 31 , publicar um serviço que outros aplicativos podem iniciar diretamente é fundamentalmente inseguro. Você pode atenuar um pouco isso tratando todas as ações de início desse serviço como exigentes
startForeground()
, embora obviamente isso possa não ser o que você tinha em mente.Rastreador de problemas do Google Comentário 56
Existem alguns cenários diferentes que levam ao mesmo resultado aqui.
A questão semântica definitiva, de que é simplesmente um erro iniciar algo,
startForegroundService()
mas negligenciar a transição para o primeiro planostartForeground()
, é apenas isso: uma questão semântica. Isso é tratado como um bug de aplicativo, intencionalmente. Parar o serviço antes de fazer a transição para o primeiro plano é um erro de aplicativo. Esse foi o cerne do OP e é por isso que esse problema foi marcado como "funcionando como pretendido".No entanto, também há perguntas sobre a detecção espúria desse problema. Isso está sendo tratado como um problema genuíno, embora esteja sendo rastreado separadamente desse problema específico do rastreador de erros. Não somos surdos à queixa.
fonte
Eu pesquisei sobre isso por alguns dias e consegui a solução. Agora, no Android O, você pode definir a limitação do fundo como abaixo
O serviço que está chamando uma classe de serviço
e a classe de serviço deve ser como
fonte
Eu tenho um widget que faz atualizações relativamente frequentes quando o dispositivo está ativado e eu estava vendo milhares de falhas em apenas alguns dias.
O gatilho do problema
Até notei o problema no meu Pixel 3 XL, quando não achei que o dispositivo tivesse muita carga. E todo e qualquer caminho de código foi coberto
startForeground()
. Mas então percebi que, em muitos casos, meu serviço realiza o trabalho muito rapidamente. Acredito que o gatilho do meu aplicativo foi que o serviço estava terminando antes que o sistema realmente mostrasse uma notificação.A solução alternativa / solução
Consegui me livrar de todas as falhas. O que fiz foi remover a chamada para
stopSelf()
. (Eu estava pensando em adiar a parada até ter certeza de que a notificação foi exibida, mas não quero que o usuário a veja se não for necessário.) Quando o serviço estiver inativo por um minuto ou o sistema destrói-o normalmente sem lançar nenhuma exceção.fonte
Apenas um alerta, porque eu perdi muitas horas com isso. Eu continuava recebendo essa exceção, embora estivesse ligando
startForeground(..)
como a primeira coisa a entraronCreate(..)
. No final, descobri que o problema foi causado pelo usoNOTIFICATION_ID = 0
. Usar qualquer outro valor parece corrigir isso.fonte
Eu tenho uma solução alternativa para esse problema. Eu verifiquei essa correção no meu próprio aplicativo (300K + DAU), que pode reduzir pelo menos 95% desse tipo de falha, mas ainda não pode 100% evitar esse problema.
Esse problema ocorre mesmo quando você liga para startForeground () logo após o início do serviço, conforme documentado pelo Google. Pode ser porque o processo de criação e inicialização do serviço já custa mais de 5 segundos em muitos cenários; não importa quando e onde você chama o método startForeground (), essa falha é inevitável.
Minha solução é garantir que startForeground () seja executado dentro de 5 segundos após o método startForegroundService (), não importa quanto tempo seu serviço precise ser criado e inicializado. Aqui está a solução detalhada.
Não use startForegroundService em primeiro lugar, use bindService () com o sinalizador auto_create. Ele aguardará a inicialização do serviço. Aqui está o código, meu serviço de amostra é MusicService:
Então aqui está a implementação do MusicBinder:
A parte mais importante, implementação de MusicService, método forceForeground () garantirá que o método startForeground () seja chamado logo após startForegroundService ():
Se você deseja executar o trecho de código da etapa 1 com uma intenção pendente, como se deseja iniciar um serviço em primeiro plano em um widget (um clique no botão do widget) sem abrir o aplicativo, é possível agrupar o trecho de código em um receptor de transmissão e dispare um evento de transmissão em vez de iniciar o comando service.
Isso é tudo. Espero que ajude. Boa sorte.
fonte
Este erro também ocorre no Android 8 ou superior quando Service.startForeground (id int, notificação de notificação) é chamado enquanto id é definido como 0.
fonte
Como todo mundo que visita aqui está sofrendo a mesma coisa, quero compartilhar minha solução que ninguém mais tentou antes (nesta pergunta de qualquer maneira). Posso garantir que ele está funcionando, mesmo em um ponto de interrupção parado, que confirma esse método.
A questão é ligar
Service.startForeground(id, notification)
do próprio serviço, certo? Infelizmente, o Android Framework não garante a chamadaService.startForeground(id, notification)
dentroService.onCreate()
de 5 segundos, mas lança a exceção de qualquer maneira, então eu vim dessa maneira.Context.startForegroundService()
Context.startForegroundService()
partir da conexão de serviço e chame imediatamenteService.startForeground()
dentro da conexão de serviço.Context.bindService()
método dentro de um try-catch porque, em algumas ocasiões, a chamada pode gerar uma exceção; nesse caso, você precisa confiarContext.startForegroundService()
diretamente na chamada e espero que não falhe. Um exemplo pode ser um contexto de receptor de transmissão, no entanto, a obtenção do contexto do aplicativo não gera uma exceção nesse caso, mas a utilização direta do contexto.Isso funciona mesmo quando estou aguardando um ponto de interrupção após ligar o serviço e antes de acionar a chamada "startForeground". Esperar entre 3-4 segundos não aciona a exceção, e após 5 segundos lança a exceção. (Se o dispositivo não puder executar duas linhas de código em 5 segundos, é hora de jogar isso no lixo.)
Portanto, comece criando uma conexão de serviço.
Dentro do seu serviço, crie um fichário que retorne a instância do seu serviço.
Em seguida, tente vincular o serviço a partir desse contexto. Se for bem-sucedido, ele chamará o
ServiceConnection.onServiceConnected()
método da conexão de serviço que você está usando. Em seguida, lide com a lógica no código mostrado acima. Um código de exemplo ficaria assim:Parece estar funcionando nos aplicativos que eu desenvolvo, portanto deve funcionar quando você tentar também.
fonte
Problema com o Android O API 26
Se você interromper o serviço imediatamente (para que seu serviço realmente não seja executado (redação / compreensão) e você esteja dentro do intervalo ANR, ainda precisará chamar startForeground antes de stopSelf
https://plus.google.com/116630648530850689477/posts/L2rn4T6SAJ5
Tentei esta abordagem, mas ainda cria um erro: -
Estou usando isso até que o erro seja resolvido
fonte
Estou enfrentando o mesmo problema e depois de passar algum tempo encontrei uma solução, você pode tentar o código abaixo. Se você estiver usando
Service
, coloque esse código em onCreate; caso contrárioIntent Service
, use esse código em onHandleIntent.fonte
Estive pesquisando esse problema e foi isso que descobri até agora. Essa falha pode ocorrer se tivermos um código semelhante a este:
MyForegroundService.java
MainActivity.java
A exceção é lançada no seguinte bloco do código:
ActiveServices.java
Este método é executado antes
onCreate()
deMyForegroundService
porque horários Android a criação do serviço no manipulador de thread principal, masbringDownServiceLocked
é chamado em umBinderThread
, wich é uma condição de corrida. Isso significa queMyForegroundService
não teve a chance de ligar, ostartForeground
que causará o acidente.Para consertar isso, precisamos garantir que isso
bringDownServiceLocked
não seja chamado antesonCreate()
deMyForegroundService
.Usando transmissões indesejadas temos certeza de que a transmissão não se perde e
stopReceiver
recebe a parada intenção assim que foi registrado emonCreate()
deMyForegroundService
. A essa altura, já ligamosstartForeground(...)
. Também precisamos remover essa transmissão pegajosa para impedir que o stopReceiver seja notificado na próxima vez.Observe que o método
sendStickyBroadcast
está obsoleto e eu o uso apenas como uma solução temporária para corrigir esse problema.fonte
Tantas respondem, mas nenhuma funcionou no meu caso.
Eu comecei o serviço assim.
E no meu serviço no onStartCommand
E não se esqueça de definir NOTIFICATION_ID diferente de zero
Então, tudo estava perfeito, mas ainda estava travando no 8.1, então a causa era a seguinte.
Chamei stop foreground com notificação de remoção, mas uma vez que o serviço de notificação removida se torne em segundo plano e o serviço em segundo plano não possa ser executado no Android O a partir de segundo plano. iniciado após o envio recebido.
Palavra tão mágica é
Até agora, qualquer motivo pelo qual seu serviço esteja falhando, siga todas as etapas acima e aproveite.
fonte
startForeground
ser chamadoonCreate
?https://developer.android.com/reference/android/content/Context.html#startForegroundService(android.content.Intent)
certifique-se de ligar o
Service.startForeground(int, android.app.Notification)
onCreate () para garantir que ele sejaContext.startService(Intent)
chamadoService.startForeground(int, android.app.Notification)
.. se você tiver alguma condição que possa impedir você de fazer isso, é melhor usar o normal e ligar para você mesmo.Parece que ele
Context.startForegroundService()
adiciona um cão de guarda para garantir que você ligouService.startForeground(int, android.app.Notification)
antes de ser destruído ...fonte
Por favor, não chame nenhum StartForgroundServices dentro do método onCreate () , você deve chamar os serviços StartForground em onStartCommand () depois de criar o thread de trabalho, caso contrário você receberá ANR sempre, portanto, não escreva login complexo no thread principal de onStartCommand () ;
EDIT: Cuidado! A função startForeground não pode aceitar 0 como primeiro argumento; ela gera uma exceção! neste exemplo contém chamada de função incorreta, altere 0 para sua própria const, que não poderia ser 0 ou ser maior que Max (Int32)
fonte
Mesmo depois de chamar o
startForeground
emService
, Ele trava em alguns dispositivos, se nós chamamosstopService
imediatamente antesonCreate
é chamado. Portanto, corrigi esse problema iniciando o serviço com um sinalizador adicional:e adicionou um check-in noStartCommand para ver se foi iniciado:
PS Se o serviço não estivesse em execução, ele seria iniciado primeiro, o que é uma sobrecarga.
fonte
Você deve adicionar uma permissão abaixo para o dispositivo Android 9 quando usar o target sdk 28 ou posterior ou a exceção sempre ocorrerá:
fonte
Acabei de verificar o
PendingIntent
nulo ou não antes de chamar acontext.startForegroundService(service_intent)
função.isso funciona para mim
fonte
basta chamar o método startForeground imediatamente após a criação do Service ou IntentService. como isso:
fonte
Ok, algo que notei sobre isso que pode ajudar alguns outros também. Isso é estritamente de teste para ver se eu consigo descobrir como corrigir as ocorrências que estou vendo. Por uma questão de simplicidade, digamos que eu tenho um método que chama isso do apresentador.
Isso falhará com o mesmo erro. O Serviço NÃO será iniciado até que o método esteja completo, portanto, não
onCreate()
no serviço.Portanto, mesmo se você atualizar a interface do usuário do thread principal, se tiver algo que possa sustentar esse método depois dele, ele não iniciará a tempo e fornecerá o temido erro de primeiro plano. No meu caso, estávamos carregando algumas coisas em uma fila e cada uma foi chamada
startForegroundService
, mas alguma lógica estava envolvida com cada uma delas em segundo plano. Portanto, se a lógica demorar muito para concluir esse método, uma vez que foram chamados de volta, o tempo de falha. O velhostartService
simplesmente ignorou e seguiu seu caminho, e desde que o chamamos cada vez, a próxima rodada terminava.Isso me deixou pensando, se eu chamasse o serviço de um thread em segundo plano, ele não poderia ser totalmente vinculado no início e executado imediatamente, então comecei a experimentar. Mesmo que isso NÃO o inicie imediatamente, ele não trava.
Não vou fingir saber por que ele não falha, embora eu suspeite que isso o force a esperar até que o thread principal possa lidar com isso em tempo hábil. Sei que não é ideal vinculá-lo ao segmento principal, mas como meu uso chama isso em segundo plano, não estou realmente preocupado se ele esperar até que seja concluído em vez de travar.
fonte
Estou adicionando algum código na resposta @humazed. Portanto, não há notificação inicial. Pode ser uma solução alternativa, mas funciona para mim.
Estou adicionando transparentColor em ícone pequeno e cor na notificação. Vai funcionar.
fonte
Corrigi o problema ao iniciar o serviço em
startService(intent)
vez deContext.startForeground()
ligarstartForegound()
imediatamente depoissuper.OnCreate()
. Além disso, se você iniciar o serviço na inicialização, poderá iniciar a Atividade que inicia o serviço na transmissão de inicialização. Embora não seja uma solução permanente, funciona.fonte
Atualizando dados no
onStartCommand(...)
onBind (...)
onBind(...)
é um evento de ciclo de vida melhor para iniciarstartForeground
vs.onCreate(...)
porqueonBind(...)
passa em umIntent
que pode conter dados importantes noBundle
necessário para inicializar oService
. No entanto, não é necessário, comoonStartCommand(...)
é chamado quando oService
é criado pela primeira vez ou chamado depois.onStartCommand (...)
startForeground
emonStartCommand(...)
é importante, a fim de atualizar oService
uma vez que já foi criado.Quando
ContextCompat.startForegroundService(...)
é chamado depois que umService
foi criadoonBind(...)
eonCreate(...)
não é chamado. Portanto, os dados atualizados podem ser transmitidosonStartCommand(...)
através doIntent
Bundle
para atualizar dados noService
.Amostra
Eu estou usando esse padrão para implementar o
PlayerNotificationManager
no Coinverse aplicativo de notícias criptomoeda.Activity / Fragment.kt
AudioService.kt
fonte
Serviço
Testador
Resultado
fonte