Remarque: J'ai essayé diverses solutions qui sont écrites ici sur StackOverflow (exemple ici ). Veuillez ne pas fermer cela sans vérifier si votre solution à partir de ce que vous avez trouvé fonctionne en utilisant le test que j'ai écrit ci-dessous.
Contexte
Il existe une exigence sur l'application, que l'utilisateur définit un rappel à planifier à une heure spécifique, donc lorsque l'application est déclenchée à ce moment, elle fait quelque chose de minuscule en arrière-plan (juste une opération de requête DB), et affiche un simple notification, pour parler du rappel.
Dans le passé, j'utilisais un code simple pour définir quelque chose à planifier à une heure relativement spécifique:
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
}
}
Usage:
val timeToTrigger = System.currentTimeMillis() + java.util.concurrent.TimeUnit.MINUTES.toMillis(1)
setAlarm(this, timeToTrigger, 1)
Le problème
J'ai maintenant testé ce code sur des émulateurs sur de nouvelles versions d'Android et sur Pixel 4 avec Android 10, et il ne semble pas se déclencher, ou peut-être qu'il se déclenche très longtemps après ce que je lui ai fourni. Je suis bien conscient du comportement terrible que certains OEM ont ajouté à la suppression des applications des tâches récentes, mais celle-ci se trouve à la fois sur les émulateurs et sur le périphérique Pixel 4 (stock).
J'ai lu dans les documents sur la définition d'une alarme, qu'elle a été restreinte pour les applications afin qu'elle ne se produise pas trop souvent, mais cela n'explique pas comment définir une alarme à un moment précis, et cela n'explique pas comment se fait de Google app de l' horloge réussit à le faire.
Non seulement cela, mais d'après ce que je comprends, cela dit que les restrictions devraient être appliquées en particulier pour l'état de faible puissance de l'appareil, mais dans mon cas, je n'avais pas cet état, à la fois sur l'appareil et sur les émulateurs. J'ai réglé les alarmes pour qu'elles se déclenchent dans environ une minute à partir de maintenant.
Voyant que de nombreuses applications de réveil ne fonctionnent plus comme avant, je pense qu'il manque quelque chose dans les documents. Un exemple de telles applications est l' application Timely populaire qui a été achetée par Google mais n'a jamais reçu de nouvelles mises à jour pour gérer les nouvelles restrictions, et maintenant les utilisateurs veulent la récupérer. . Cependant, certaines applications populaires fonctionnent bien, comme celle-ci .
Ce que j'ai essayé
Pour tester le fait que l'alarme fonctionne, j'effectue ces tests lorsque j'essaie de déclencher l'alarme dans une minute à partir de maintenant, après avoir installé l'application pour la première fois, le tout pendant que l'appareil est connecté au PC (pour voir les journaux):
- Testez lorsque l'application est au premier plan, visible par l'utilisateur. - a pris 1-2 minutes.
- Test lorsque l'application a été envoyée en arrière-plan (en utilisant le bouton d'accueil, par exemple) - a pris environ 1 minute
- Testez quand la tâche de l'application a été supprimée des tâches récentes. - J'ai attendu plus de 20 minutes et je n'ai pas vu l'alarme se déclencher, écrivant dans les journaux.
- Comme # 3, mais aussi éteignez l'écran. Ce serait probablement pire ...
J'ai essayé d'utiliser les choses suivantes, tout ne fonctionne pas:
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
combinaison de l'un des éléments ci-dessus, avec:
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) alarmManager.setWindow(AlarmManager.RTC_WAKEUP, 0, 60 * 1000L, pendingIntent)
J'ai essayé d'utiliser un service au lieu de BroadcastReceiver. Également essayé sur un processus différent.
J'ai essayé de faire en sorte que l'application soit ignorée de l'optimisation de la batterie (n'a pas aidé), mais comme d'autres applications n'en ont pas besoin, je ne devrais pas l'utiliser non plus.
Essayé en utilisant ceci:
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP)
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
- J'ai essayé d'avoir un service qui aura un déclencheur de onTaskRemoved , pour replanifier l'alarme là-bas, mais cela n'a pas aidé non plus (le service a bien fonctionné cependant).
Quant à l'application Horloge de Google, je n'ai rien vu de spécial à part qu'elle montre une notification avant d'être déclenchée, et je ne la vois pas non plus dans la section "non optimisée" de l'écran des paramètres d'optimisation de la batterie.
Voyant que cela semble être un bug, j'ai signalé à ce sujet ici , y compris un exemple de projet et une vidéo pour montrer le problème.
J'ai vérifié plusieurs versions de l'émulateur, et il semble que ce comportement soit parti de l'API 27 (Android 8.1 - Oreo). En regardant les documents , je ne vois pas que AlarmManager soit mentionné, mais à la place, il a été écrit sur divers travaux d'arrière-plan.
Questions
Comment définissons-nous quelque chose à déclencher à une heure relativement exacte de nos jours?
Comment se fait-il que les solutions ci-dessus ne fonctionnent plus? Suis-je en train de manquer quelque chose? Autorisation? Peut-être que je suis censé utiliser un Worker à la place? Mais cela ne signifierait-il pas que cela pourrait ne pas se déclencher du tout?
Comment l'application "Horloge" de Google surmonte-t-elle tout cela et se déclenche-t-elle de toute façon à l'heure exacte, toujours, même si elle a été déclenchée il y a une minute? Est-ce uniquement parce que c'est une application système? Que faire si elle est installée en tant qu'application utilisateur, sur un appareil qui ne l'a pas intégré?
Si vous dites que c'est parce que c'est une application système, j'ai trouvé une autre application qui peut déclencher une alarme deux fois en 2 minutes, ici , bien que je pense qu'elle utilise parfois un service de premier plan.
EDIT: créé un petit dépôt Github pour essayer des idées, ici .
EDIT: a finalement trouvé un échantillon qui est à la fois open-source et n'a pas ce problème. Malheureusement, c'est très complexe et j'essaie toujours de comprendre ce qui le rend si différent (et quel est le code minimal que je devrais ajouter à mon POC) qui permet à ses alarmes de rester programmées après la suppression de l'application des tâches récentes
la source
Réponses:
Nous n'avons rien à faire.
Une fois que votre application n'est pas sur liste blanche, elle sera toujours supprimée une fois supprimée des applications récentes.
Parce que le fabricant d'équipement d'origine (OME) viole constamment la conformité Android .
Donc, si votre application n'est pas ajoutée à la liste blanche de la fabrication de l'appareil, elle ne déclenchera aucun travail en arrière-plan, même des alarmes - au cas où votre application serait supprimée des applications récentes.
Vous pouvez trouver une liste d'appareils avec ce comportement ici AUSSI vous pourriez trouver une solution secondaire, Cependant, cela ne fonctionnera pas bien.
la source
Trouvé une solution de contournement étrange (exemple ici ) qui semble fonctionner pour toutes les versions, y compris même Android R:
Sur Android R, vous devrez également l'avoir. Sur avant, il ne semble pas que cela devait être accordé, vient d'être déclaré. Je ne sais pas pourquoi cela a changé sur R, mais je peux dire que SAW pourrait être nécessaire comme solution possible pour démarrer les choses en arrière-plan, comme écrit ici pour Android 10.
FakeActivity.kt
Vous pouvez également rendre cette activité presque invisible pour l'utilisateur en utilisant ce thème:
Malheureusement, c'est une solution de contournement étrange. J'espère trouver une meilleure solution à cela.
La restriction parle de démarrer l'activité, donc mon idée actuelle est que si je démarre un service de premier plan pendant une fraction de seconde, cela aidera également, et pour cela, je n'aurai même pas besoin de l'autorisation SAW.
EDIT: OK, j'ai essayé avec un service de premier plan (exemple ici ), et cela n'a pas fonctionné. Aucune idée pourquoi une activité fonctionne mais pas un service. J'ai même essayé de reprogrammer l'alarme là-bas et j'ai essayé de laisser le service rester un peu, même après avoir reprogrammé. A également essayé un service normal, mais bien sûr, il a fermé immédiatement, car la tâche a été supprimée, et cela n'a pas fonctionné du tout (même si j'ai créé un thread à exécuter en arrière-plan).
Une autre solution possible que je n'ai pas essayée est d'avoir un service de premier plan pour toujours, ou du moins jusqu'à ce que la tâche soit supprimée, mais c'est un peu bizarre et je ne vois pas les applications que j'ai mentionnées l'utiliser.
EDIT: essayé de faire fonctionner un service de premier plan avant la suppression de la tâche de l'application, et pendant un peu après, et l'alarme fonctionnait toujours. A également essayé d'avoir ce service pour être celui en charge de l'événement supprimé par la tâche, et de se fermer immédiatement quand il se produit, et cela a toujours fonctionné (exemple ici ). L'avantage de cette solution de contournement est que vous n'avez pas du tout besoin de l'autorisation SAW. L'inconvénient est que vous disposez d'un service avec une notification alors que l'application est déjà visible pour l'utilisateur. Je me demande s'il est possible de masquer la notification alors que l'application est déjà au premier plan via l'activité.
EDIT: Il semble que ce soit un bug sur Android Studio (signalé ici , y compris les vidéos comparant les versions). Lorsque vous lancez l'application à partir de la version problématique que j'ai essayée, cela pourrait entraîner la suppression des alarmes.
Si vous lancez l'application à partir du lanceur, cela fonctionne très bien.
Voici le code actuel pour régler l'alarme:
Je n'ai même pas besoin d'utiliser "pendingShowList". L'utilisation de null est également correcte.
la source
SYSTEM_ALERT_WINDOW
permission?Intent.FLAG_RECEIVER_FOREGROUND
drapeau.https://developer.android.com/about/versions/oreo/background#broadcasts
setExactAndAllowWhileIdle()
lors du ciblage de l'API 23+.https://developer.android.com/about/versions/oreo/background#migration
la source
Je sais que ce n'est pas efficace mais pourrait être plus cohérent avec une précision de 60 secondes.
https://developer.android.com/reference/android/content/Intent#ACTION_TIME_TICK
si ce récepteur de diffusion est utilisé à l'intérieur d'un service de premier plan, vous pouvez vérifier l'heure toutes les minutes et prendre la décision de prendre une mesure.
la source
Je pense que vous pouvez demander à l'utilisateur de définir l'autorisation afin qu'il désactive le mode d'économie d'énergie et avertir l'utilisateur que s'il ne l'utilise pas, les heures exactes ne seront pas atteintes.
Voici le code pour le demander:
la source
Je suis l'auteur du projet open source que vous avez évoqué dans votre question ( simple réveil) .
Je suis surpris que l'utilisation de AlarmManager.setAlarmClock n'ait pas fonctionné pour vous, car mon application fait exactement cela. Le code se trouve dans le fichier AlarmSetter.kt. Voici un extrait:
Fondamentalement, cela n'a rien de spécial, assurez-vous simplement que l'intention a une action et une classe cible, qui est un récepteur de diffusion dans mon cas.
la source