Nota: Probé varias soluciones sobre las que se escribe aquí en StackOverflow (ejemplo aquí ). No cierre esto sin verificar si su solución de lo que ha encontrado funciona con la prueba que he escrito a continuación.
Antecedentes
Hay un requisito en la aplicación, que el usuario establezca un recordatorio para que se programe en un momento específico, por lo que cuando la aplicación se activa en este momento, hace algo pequeño en segundo plano (solo una operación de consulta DB) y muestra un Notificación simple, para informar sobre el recordatorio.
En el pasado, utilicé un código simple para configurar algo que se programaría en un momento 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)
El problema
Ahora he probado este código en emuladores en nuevas versiones de Android y en Pixel 4 con Android 10, y parece que no se dispara, o tal vez se dispara después de mucho tiempo desde que lo proporcioné. Soy consciente del terrible comportamiento que algunos fabricantes de equipos originales agregaron para eliminar aplicaciones de las tareas recientes, pero este se encuentra tanto en emuladores como en dispositivos Pixel 4 (stock).
Leí en los documentos sobre la configuración de una alarma, que se restringió para las aplicaciones, por lo que no ocurrirá con demasiada frecuencia, pero esto no explica cómo configurar una alarma en un momento específico, y no explica ¿Cómo es que la aplicación Reloj de Google tiene éxito al hacerlo?
No solo eso, sino que según lo que entiendo, dice que las restricciones deberían aplicarse especialmente para el estado de baja potencia del dispositivo, pero en mi caso, no tenía este estado, tanto en el dispositivo como en los emuladores. He configurado las alarmas para que se activen en aproximadamente un minuto a partir de ahora.
Al ver que muchas aplicaciones de despertador ya no funcionan como solían hacerlo, creo que hay algo que falta en los documentos. Ejemplo de tales aplicaciones es la popular aplicación Timely que fue comprada por Google pero nunca recibió nuevas actualizaciones para manejar las nuevas restricciones, y ahora los usuarios quieren recuperarla. . Sin embargo, algunas aplicaciones populares funcionan bien, como esta .
Lo que he intentado
Para probar que la alarma funciona, realizo estas pruebas cuando intento activar la alarma en un minuto a partir de ahora, después de instalar la aplicación por primera vez, todo mientras el dispositivo está conectado a la PC (para ver los registros):
- Pruebe cuando la aplicación esté en primer plano, visible para el usuario. - tomó 1-2 minutos.
- Pruebe cuándo se envió la aplicación a un segundo plano (usando el botón de inicio, por ejemplo): tomó aproximadamente 1 minuto
- Pruebe cuándo se eliminó la tarea de la aplicación de las tareas recientes. - Esperé más de 20 minutos y no vi que se activara la alarma, escribiendo en los registros.
- Como # 3, pero también apaga la pantalla. Probablemente sería peor ...
Traté de usar las siguientes cosas, todas no funcionan:
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
combinación de cualquiera de los anteriores, con:
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) alarmManager.setWindow(AlarmManager.RTC_WAKEUP, 0, 60 * 1000L, pendingIntent)
Intenté usar un servicio en lugar de BroadcastReceiver. También probé en un proceso diferente.
Intenté ignorar la aplicación desde la optimización de la batería (no ayudó), pero como otras aplicaciones no la necesitan, tampoco debería usarla.
Intenté usar esto:
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP)
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
- Intenté tener un servicio que activará onTaskRemoved , para reprogramar la alarma allí, pero esto tampoco ayudó (aunque el servicio funcionó bien).
En cuanto a la aplicación Reloj de Google, no vi nada especial al respecto, excepto que muestra una notificación antes de activarse, y tampoco la veo en la sección "no optimizada" de la pantalla de configuración de optimización de la batería.
Al ver que esto parece un error, informé sobre esto aquí , incluido un proyecto de muestra y un video para mostrar el problema.
He comprobado varias versiones del emulador, y parece que este comportamiento comenzó desde la API 27 (Android 8.1 - Oreo). Mirando los documentos , no veo que se mencione AlarmManager, sino que se escribió sobre varios trabajos de fondo.
Las preguntas
¿Cómo establecemos que algo se active en un momento relativamente exacto hoy en día?
¿Cómo es que las soluciones anteriores ya no funcionan? ¿Me estoy perdiendo algo? ¿Permiso? ¿Quizás se supone que debo usar un trabajador en su lugar? Pero entonces, ¿no significaría que podría no activarse a tiempo?
¿Cómo supera la aplicación "Reloj" de Google todo esto y se activa de todos modos en la hora exacta, siempre, incluso si se activó hace solo un minuto? ¿Es solo porque es una aplicación del sistema? ¿Qué sucede si se instala como una aplicación de usuario, en un dispositivo que no lo tiene incorporado?
Si usted dice que es porque es una aplicación del sistema, he encontrado otra aplicación que puede disparar una alarma dos veces en 2 minutos, aquí , aunque creo que podría utilizar un servicio de plano algunas veces.
EDITAR: hizo un pequeño repositorio de Github para probar ideas, aquí .
EDITAR: finalmente encontró una muestra que es de código abierto y no tiene este problema. Lamentablemente es muy complejo y todavía trato de averiguar qué lo hace tan diferente (y cuál es el código mínimo que debo agregar a mi POC) que permite que sus alarmas permanezcan programadas después de eliminar la aplicación de las tareas recientes