Introdução
A medida que novas versões do android vão surgindo, são criadas diferentes formas de trabalho e acesso aos dados do ecossistema. Diante disso, desde a versão do android 6, a Google vem tratando a forma que os serviços são executados, visando uma boa experiência do usuário e diminuindo riscos em potenciais.
A Google também avançou em diversas ferramentas para trabalhos em segundo plano e agendamentos de tarefas, no qual podemos citar: AlarmManager, WorkManager(oficial) e Firebase JobDispatcher. A medida que crescia a popularidade dessas ferramentas, a preocupação com o consumo de bateria também aumentava.
Em suma, o consumo de bateria diminui quando estamos executando alguma tarefa em segundo plano, visto que a execução dela pode ser feita de diferentes formatos, desde uma vez a cada hora e até de maneira recorrente com poucos intervalos de 10 segundos.
Com isso, a partir do android 6, foi introduzido o modo Soneca e amplamente apoiado pela comunidade.
O que é o modo soneca?
O modo soneca no android é uma forma de economizar bateria quando o dispositivo está muito tempo sem interação, ou seja, o celular só entra neste modo quando o usuário não utiliza. Sendo assim, o modo soneca entende que o dispositivo não está sendo utilizado e barra qualquer alarme, notificação, acesso a dados remotos e até tarefas que estão sendo executados em segundo plano, mas com uma condição: Essas tarefas, alarmes, notificações e etc são executados, mas em um formato de janela e varia com o tempo.
O que são as janelas de execução?
As janelas de execução são feitas para poder executar qualquer tarefa quando o dispositivo estiver no modo soneca. A partir da janela, o sistema operacional aciona algumas tarefas e coloca em uma janela. Com isso, todas as tarefas são executadas naquele período. O problema é que a medida que uma janela é colocada, a próxima executa em um tempo maior que a outra, sendo assim, a tarefa que era para ser executada a cada 30 segundos, naquela janela ela será executada daqui a 30 minutos e prejudicando o seu aplicativo.
AlarmManager
Não vamos nos aprofundar nesse momento com o alarmManager, mas é necessário termos um breve conhecimento.
Segundo a documentação do AlarmManager, ele é uma ferramenta para realizar operações baseadas em tempo fora da vida útil do seu app. Em qualquer projeto tem seu caso de uso, mas podemos imaginar uma o conceito de despertador, mas imaginemos o seguinte: o despertador só sabe que precisa tocar todos os dias as 16:30 horas porque foi programado através do alarmManager para ele realizar aquela operação.
Problemática
Temos um seguinte problema e precisamos resolver ele urgente: Um grupo de desenvolvedores criaram um aplicativo que gerencia o tempo de estudos dos usuários. Logo após o desenvolvimento, eles notaram que aqueles estudantes que passavam um bom tempo sem o celular começaram a reclamar que o dispositivo não estava tocando no horário programado e ocasionando em tempos indevidos de estudos.
Basicamente, é isso que vamos resolver aqui. Será criado uma solução para este problema e fazer com que o dispositivo toque mesmo no modo soneca.
Implementação
Criando um projeto android, iremos criar a classe IdleAlarmManager, onde fica responsável por configurar toda ação de alarme do aplicativo.
- IdleAlarmManager
class IdleAlarmManager(context: Context) {
private val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
private val alarmIntent =
PendingIntent.getBroadcast(context, 0, Intent(context, AlarmReceiver::class.java), 0)
private val calendar = Calendar.getInstance().apply {
timeInMillis = System.currentTimeMillis()
set(Calendar.HOUR_OF_DAY, SUA_HORA)
set(Calendar.MINUTE, SEU_MINUTO)
}
fun setAlarmManager() = alarmManager.setExactAndAllowWhileIdle (
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis,
alarmIntent
)
}
Explicação do código acima:
- É criado uma pendingIntent e passando qual broadcast receiver deve ser chamado quando o alarme for disparado.
- O objeto calendar é utilizado para definir o horário que o alarme será disparado.
- A parte mais importante do código: O metódo setAlarmManager() faz a configuração do alarme e a sua característica, no nosso caso, um alarme que dispara mesmo no modo soneca.
- o metódo setExactAndAllowWhileIdle() garante que o alarme será chamado mesmo quando estiver no modo soneca, diferente do setAndAllowWhileIdle(), no qual não tem total garantia. Este método exato ou o outro deve ser chamado para que o alarme seja tocado. Se for usado qualquer outro método, só será disparado quando o dispositivo sair do modo soneca. Lembrando que ambos podem haver atrasos em disparar esse alarme.
Depois, para mostrar a notificação após o alarme ser executado, será necessário criar um broadcast receiver e registrar no manifest. O broadcast receiver permite que um aplicativo receba mensagens ou "broadcasts" do sistema operacional ou de outros aplicativos, mesmo quando o aplicativo não está em execução. O metódo onReceive() é chamado toda vez que recebe uma mensagem do sistema.
- Android Manifest
<receiver android:name="com.github.tumusx.idle.alarmManager.AlarmReceiver"/>
- AlarmReceiver
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val builder = NotificationCompat.Builder(context, SEU_CHANNEL_ID)
.setSmallIcon(R.drawable.notification)
.setContentTitle(SEU_TITULO)
.setContentText(SUA_MENSAGEM)
.setStyle(
NotificationCompat.BigTextStyle()
.bigText(SUA_MENSAGEM)
)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
val name = SEU_NOME
val description = SUA_DESCRICAO
val channelNotification = NotificationChannel(
SEU_CHANNEL_ID,
name,
NotificationManager.IMPORTANCE_HIGH,
).also {
it.description = description
}
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channelNotification)
notificationManager.notify(SEU_CHANNEL_ID, builder.build())
}
Não irei entrar em detalhes sobre o código acima pois não é o objetivo tratar notificações, mas em resumo, toda vez que o alarme for disparado, será construído e mostrado uma notificação para o usuário.
NÃO É POSSÍVEL ALTERAR PROGRAMATICAMENTE O MODO SONECA NO ANDROID
Para testar se realmente o alarme está sendo despertado no modo soneca, é necessário executar alguns comandos para forçar o dispositivo a entrar no modo soneca:
Com o seguinte caminho, abra o cmd:
C:\Users\SEU_USER\AppData\Local\Android\Sdk\platform-tools>
- adb shell dumpsys deviceidle enable: Força a entrar em modo econômico;
- adb shell dumpsys deviceidle force-idle: Força a entrar em modo soneca;
- adb shell dumpsys deviceidle get deep: Checa o estado atual do dispositivo. Se retornar a mensagem IDLE, ele está em modo soneca;
- adb shell dumpsys deviceidle unforce: Remove o dispositivo do modo soneca.
Caso queira consultar o repositório, se encontra no github.
Considerações finais
Como foi mostrado, é possível sim executar alguns processos mesmo quando o dispositivo estiver no modo soneca. Lembrando que o uso dessa implementação, o modo soneca não é desativado. Apenas garante que o alarme seja disparado quando estiver neste modo.
Existe outra solução para o problema, mas é utilizado o Firebase Cloud Messaging e a notificação tem que ser definida como prioridade alta.
Contate-me
Linkedin
Top comments (2)
Bom dia
sou iniciante, estou fazendo um alarme com notificação e estou com um problema
quando minimizo o app ou desligo a tela por muito tempo ele não desperta,
como e um despertador para tomar remédio, eu preciso que desperte na hora exata,
já fiz de vários jeitos diferente, você poderia me ajudar?
Opa. boa noite
Como você está implementando o seu alarmManager? Verificou se está com modo economia de bateria ligado? Ou até melhor, está usando o setExactAndAllowWhileIdle, assim como no tutorial?