Nota: Tentei várias soluções descritas aqui no StackOverflow (exemplo aqui ). Não feche isso sem verificar se sua solução do que você encontrou funciona usando o teste que escrevi abaixo.
fundo
Há um requisito no aplicativo, que o usuário defina um lembrete para ser agendado em um horário específico; portanto, quando o aplicativo é acionado nesse momento, ele faz algo minúsculo em segundo plano (apenas algumas operações de consulta ao banco de dados) e mostra um notificação simples, para contar sobre o lembrete.
No passado, usei um código simples para definir algo a ser agendado em um horário relativamente específico:
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val pendingIntent = PendingIntent.getBroadcast(context, requestId, Intent(context, AlarmReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
when {
VERSION.SDK_INT >= VERSION_CODES.KITKAT -> alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
else -> alarmManager.set(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
}
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("AppLog", "AlarmReceiver onReceive")
//do something in the real app
}
}
Uso:
val timeToTrigger = System.currentTimeMillis() + java.util.concurrent.TimeUnit.MINUTES.toMillis(1)
setAlarm(this, timeToTrigger, 1)
O problema
Agora testei esse código em emuladores nas novas versões do Android e no Pixel 4 com Android 10, e ele não parece ser acionado, ou talvez seja acionado após muito tempo desde o que eu forneço. Estou ciente do péssimo comportamento que alguns OEMs adicionaram para remover aplicativos das tarefas recentes, mas este é usado para emuladores e dispositivos Pixel 4 (padrão).
Eu li os documentos sobre a configuração de um alarme, que ficou restrito a aplicativos para que não ocorra com muita frequência, mas isso não explica como definir um alarme em um horário específico e não explica como é que o aplicativo Clock do Google consegue fazer isso?
Não apenas isso, mas de acordo com o que entendi, ele diz que as restrições devem ser aplicadas especialmente para o estado de baixa energia do dispositivo, mas no meu caso, eu não tinha esse estado, tanto no dispositivo como nos emuladores. Eu configurei os alarmes para serem acionados daqui a um minuto.
Como muitos aplicativos de despertador não funcionam mais como costumavam, acho que falta algo nos documentos. Exemplo desses aplicativos é o popular aplicativo Timely, que foi comprado pelo Google, mas nunca recebeu novas atualizações para lidar com as novas restrições, e agora os usuários o desejam de volta. . No entanto, alguns aplicativos populares funcionam bem, como este .
O que eu tentei
Para testar se o alarme funciona, eu realizo esses testes ao tentar disparar o alarme dentro de um minuto, depois de instalar o aplicativo pela primeira vez, enquanto o dispositivo estiver conectado ao PC (para ver os registros):
- Teste quando o aplicativo está em primeiro plano, visível para o usuário. - levou 1-2 minutos.
- Teste quando o aplicativo foi enviado para segundo plano (usando o botão de início, por exemplo) - demorou cerca de 1 minuto
- Teste quando a tarefa do aplicativo foi removida das tarefas recentes. - Esperei mais de 20 minutos e não vi o alarme sendo disparado, escrevendo nos logs.
- Como # 3, mas também desligue a tela. Provavelmente seria pior ...
Eu tentei usar as próximas coisas, nem tudo funciona:
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
combinação de qualquer dos itens acima, com:
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) alarmManager.setWindow(AlarmManager.RTC_WAKEUP, 0, 60 * 1000L, pendingIntent)
Tentei usar um serviço em vez de BroadcastReceiver. Também tentei em um processo diferente.
Tentei fazer com que o aplicativo fosse ignorado pela otimização da bateria (não ajudou), mas como outros aplicativos não precisam, também não devo usá-lo.
Tentei usar isso:
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP)
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
- Tentei ter um serviço que terá um gatilho de onTaskRemoved , para remarcar o alarme lá, mas isso também não ajudou (o serviço funcionou bem).
Quanto ao aplicativo Clock do Google, não vi nada de especial nele, exceto que ele mostra uma notificação antes de ser acionada e também não a vejo na seção "não otimizado" da tela de configurações de otimização da bateria.
Vendo que isso parece um bug, relatei sobre isso aqui , incluindo um projeto de amostra e um vídeo para mostrar o problema.
Eu verifiquei várias versões do emulador e parece que esse comportamento começou na API 27 (Android 8.1 - Oreo). Observando os documentos , não vejo o AlarmManager sendo mencionado, mas foi escrito sobre vários trabalhos em segundo plano.
As questões
Como definimos algo para ser acionado em um horário relativamente exato hoje em dia?
Como as soluções acima não funcionam mais? Estou faltando alguma coisa? Permissão? Talvez eu deva usar um trabalhador em vez disso? Mas então, isso não significaria que talvez não fosse acionado a tempo?
Como o aplicativo "Clock" do Google supera tudo isso e é acionado de qualquer maneira na hora exata, sempre, mesmo que tenha sido acionado há apenas um minuto? É apenas porque é um aplicativo do sistema? E se ele for instalado como um aplicativo de usuário, em um dispositivo que não o tenha incorporado?
Se você diz que é por ser um aplicativo do sistema, encontrei outro aplicativo que pode disparar um alarme duas vezes em 2 minutos, aqui , embora eu ache que às vezes possa usar um serviço em primeiro plano.
EDIT: criou um pequeno repositório Github para experimentar idéias, aqui .
EDIT: finalmente encontrei uma amostra que é de código aberto e não tem esse problema. Infelizmente, é muito complexo e ainda tento descobrir o que o torna tão diferente (e qual é o código mínimo que devo adicionar ao meu POC) que permite que seus alarmes permaneçam agendados após a remoção do aplicativo das tarefas recentes