Nota: ho provato varie soluzioni scritte qui su StackOverflow (esempio qui ). Non chiudere questo senza verificare se la tua soluzione da ciò che hai trovato funziona utilizzando il test che ho scritto di seguito.
sfondo
Esiste un requisito sull'app, che l'utente imposta un promemoria da programmare in un momento specifico, quindi quando l'app viene avviata in questo momento, fa qualcosa di minuscolo in background (solo alcune operazioni di query DB) e mostra un semplice notifica, per raccontare il promemoria.
In passato, ho usato un semplice codice per impostare qualcosa da programmare in un momento relativamente specifico:
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)
Il problema
Ora ho testato questo codice sugli emulatori su nuove versioni di Android e su Pixel 4 con Android 10, e non sembra innescarsi, o forse si innesca dopo molto tempo da quando l'ho fornito. Sono ben consapevole del comportamento terribile che alcuni OEM hanno aggiunto alla rimozione di app dalle attività recenti, ma questo è sia su emulatori che su dispositivo Pixel 4 (stock).
Ho letto i documenti sull'impostazione di un allarme, che è stato limitato per le app in modo che non si verifichi troppo spesso, ma questo non spiega come impostare un allarme in un momento specifico e non spiega come mai l' app di Google Clock riesce a farlo.
Non solo, ma secondo quello che ho capito, dice che le restrizioni dovrebbero essere applicate soprattutto per lo stato di basso consumo del dispositivo, ma nel mio caso, non avevo questo stato, sia sul dispositivo che sugli emulatori. Ho impostato gli allarmi per attivarli tra circa un minuto.
Visto che molte app per le sveglie non funzionano più come una volta, penso che manchi qualcosa nei documenti. Esempio di tali app è la popolare app Timely che è stata acquistata da Google ma non ha mai ricevuto nuovi aggiornamenti per gestire le nuove restrizioni e ora gli utenti la vogliono indietro. . Tuttavia, alcune app popolari funzionano bene, come questa .
Quello che ho provato
Per provare che effettivamente l'allarme funziona, eseguo questi test quando provo ad attivare l'allarme tra un minuto, dopo aver installato l'app per la prima volta, il tutto mentre il dispositivo è collegato al PC (per vedere i registri):
- Verifica quando l'app è in primo piano, visibile all'utente. - ci sono voluti 1-2 minuti.
- Il test quando l'app è stata inviata in background (utilizzando il pulsante Home, ad esempio) - ha richiesto circa 1 minuto
- Verifica quando l'attività dell'app è stata rimossa dalle attività recenti. - Ho aspettato più di 20 minuti e non ho visto l'allarme essere attivato, scrivendo nei registri.
- Come il n. 3, ma spegni anche lo schermo. Probabilmente sarebbe peggio ...
Ho provato a usare le cose successive, tutte non funzionano:
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
combinazione di uno dei precedenti, con:
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) alarmManager.setWindow(AlarmManager.RTC_WAKEUP, 0, 60 * 1000L, pendingIntent)
Ho provato a usare un servizio invece di BroadcastReceiver. Ho anche provato un processo diverso.
Ho provato a far sì che l'app fosse ignorata dall'ottimizzazione della batteria (non ha aiutato), ma poiché altre app non ne hanno bisogno, non dovrei usarla neanche.
Ho provato usando questo:
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP)
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
- Ho provato ad avere un servizio che avrà un trigger di Ho onTaskRemoved , per riprogrammare l'allarme lì, ma anche questo non ha aiutato (il servizio ha funzionato bene però).
Per quanto riguarda l'app Orologio di Google, non ho visto nulla di speciale al riguardo tranne che mostra una notifica prima di essere attivata e non la vedo nemmeno nella sezione "non ottimizzata" della schermata delle impostazioni di ottimizzazione della batteria.
Visto che questo sembra un bug, ho riferito di questo qui , incluso un progetto di esempio e un video per mostrare il problema.
Ho controllato più versioni dell'emulatore e sembra che questo comportamento sia iniziato dall'API 27 (Android 8.1 - Oreo). Guardare i documenti , non vedo menzionare AlarmManager, ma invece è stato scritto su vari lavori in background.
Le domande
Come possiamo impostare qualcosa da attivare in un momento relativamente esatto al giorno d'oggi?
Come mai le soluzioni di cui sopra non funzionano più? Mi sto perdendo qualcosa? Autorizzazione? Forse dovrei usare un lavoratore invece? Ma allora non significherebbe che potrebbe non innescarsi in tempo?
In che modo l'app "Orologio" di Google supera tutto questo e si innesca comunque all'ora esatta, sempre, anche se è stata attivata solo un minuto fa? È solo perché è un'app di sistema? Cosa succede se viene installato come app utente, su un dispositivo che non lo ha integrato?
Se dici che è perché è un'app di sistema, ho trovato un'altra app che può attivare un allarme due volte in 2 minuti, qui , anche se penso che a volte potrebbe usare un servizio di primo piano.
EDIT: realizzato un piccolo repository Github per provare idee, qui .
EDIT: finalmente trovato un campione che è sia open-source e non presenta questo problema. Purtroppo è molto complesso e cerco ancora di capire cosa lo rende così diverso (e qual è il codice minimo che dovrei aggiungere al mio POC) che consente ai suoi allarmi di essere programmati dopo aver rimosso l'app dalle attività recenti