Hinweis: Ich habe verschiedene Lösungen ausprobiert, über die hier in StackOverflow geschrieben wurde (Beispiel hier ). Bitte schließen Sie dies nicht, ohne zu überprüfen, ob Ihre Lösung von dem, was Sie gefunden haben, mit dem unten beschriebenen Test funktioniert.
Hintergrund
Für die App ist es erforderlich, dass der Benutzer eine Erinnerung festlegt, die zu einem bestimmten Zeitpunkt geplant werden soll. Wenn die App zu diesem Zeitpunkt ausgelöst wird, führt sie im Hintergrund etwas Winziges aus (nur eine DB-Abfrageoperation) und zeigt a einfache Benachrichtigung, um über die Erinnerung zu erzählen.
In der Vergangenheit habe ich einen einfachen Code verwendet, um etwas festzulegen, das zu einem relativ bestimmten Zeitpunkt geplant werden soll:
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
}
}
Verwendungszweck:
val timeToTrigger = System.currentTimeMillis() + java.util.concurrent.TimeUnit.MINUTES.toMillis(1)
setAlarm(this, timeToTrigger, 1)
Das Problem
Ich habe diesen Code jetzt auf Emulatoren in neuen Android-Versionen und auf Pixel 4 mit Android 10 getestet, und er scheint nicht auszulösen, oder er wird möglicherweise nach einer sehr langen Zeit seit dem, was ich ihm zur Verfügung stelle, ausgelöst. Ich bin mir des schrecklichen Verhaltens bewusst , das einige OEMs zum Entfernen von Apps aus den letzten Aufgaben hinzugefügt haben, aber dieses ist sowohl auf Emulatoren als auch auf Pixel 4-Geräten (Standard).
Ich habe in den Dokumenten über das Einstellen eines Alarms gelesen , dass er für Apps eingeschränkt wurde, damit er nicht zu oft auftritt, aber dies erklärt nicht, wie ein Alarm zu einem bestimmten Zeitpunkt eingestellt wird, und es erklärt nicht Wie kommt es, dass Googles Clock-App erfolgreich ist?
Darüber hinaus heißt es nach meinem Verständnis, dass die Einschränkungen insbesondere für den Energiesparmodus des Geräts gelten sollten. In meinem Fall hatte ich diesen Status jedoch weder auf dem Gerät noch auf den Emulatoren. Ich habe die Alarme so eingestellt, dass sie in ungefähr einer Minute ausgelöst werden.
Angesichts der Tatsache, dass viele Wecker-Apps nicht mehr wie früher funktionieren, fehlt meiner Meinung nach etwas in den Dokumenten. Ein Beispiel für solche Apps ist die beliebte Timely- App, die von Google gekauft wurde, aber nie neue Updates erhalten hat, um die neuen Einschränkungen zu erfüllen. Jetzt möchten Benutzer sie zurück. . Einige beliebte Apps funktionieren jedoch einwandfrei, wie z. B. diese .
Was ich versucht habe
Um zu testen, ob der Alarm tatsächlich funktioniert, führe ich diese Tests durch, wenn ich in einer Minute nach der ersten Installation der App versuche, den Alarm auszulösen, während das Gerät mit dem PC verbunden ist (um die Protokolle anzuzeigen):
- Testen Sie, ob sich die App im Vordergrund befindet und für den Benutzer sichtbar ist. - dauerte 1-2 Minuten.
- Der Test, wann die App in den Hintergrund gesendet wurde (z. B. über die Home-Taste), dauerte ca. 1 Minute
- Testen Sie, wann die Aufgabe der App aus den letzten Aufgaben entfernt wurde. - Ich habe mehr als 20 Minuten gewartet und nicht gesehen, dass der Alarm ausgelöst wurde, als ich in Protokolle schrieb.
- Wie # 3, aber auch den Bildschirm ausschalten. Es wäre wahrscheinlich schlimmer ...
Ich habe versucht, die nächsten Dinge zu verwenden, alle funktionieren nicht:
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
Kombination einer der oben genannten, mit:
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) alarmManager.setWindow(AlarmManager.RTC_WAKEUP, 0, 60 * 1000L, pendingIntent)
Es wurde versucht, einen Dienst anstelle von BroadcastReceiver zu verwenden. Auch einen anderen Prozess ausprobiert.
Ich habe versucht, die App aus der Batterieoptimierung zu ignorieren (hat nicht geholfen), aber da andere Apps sie nicht benötigen, sollte ich sie auch nicht verwenden.
Versucht damit:
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP)
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
- Es wurde versucht, einen Dienst mit dem Auslöser onTaskRemoved zu haben , um den Alarm dort neu zu planen, aber dies hat auch nicht geholfen (der Dienst hat jedoch einwandfrei funktioniert).
In Bezug auf die Google Clock-App habe ich nichts Besonderes gesehen, außer dass eine Benachrichtigung angezeigt wird, bevor sie ausgelöst wird, und ich sehe sie auch nicht im Abschnitt "Nicht optimiert" des Bildschirms mit den Einstellungen für die Batterieoptimierung.
Sehen , dass dies wie ein Bug scheint, berichten ich dies etwa hier , darunter ein Beispielprojekt und Video , das Problem zu zeigen.
Ich habe mehrere Versionen des Emulators überprüft und es scheint, dass dieses Verhalten von API 27 (Android 8.1 - Oreo) aus gestartet wurde. Wenn ich mir die Dokumente ansehe, sehe ich nicht, dass AlarmManager erwähnt wird, sondern es wurde über verschiedene Hintergrundarbeiten geschrieben.
Die Fragen
Wie stellen wir etwas ein, das heutzutage zu einem relativ genauen Zeitpunkt ausgelöst wird?
Wie kommt es, dass die oben genannten Lösungen nicht mehr funktionieren? Vermisse ich etwas Genehmigung? Vielleicht soll ich stattdessen einen Arbeiter benutzen? Aber würde es dann nicht bedeuten, dass es möglicherweise nicht rechtzeitig ausgelöst wird?
Wie überwindet die Google "Clock" -App all dies und wird trotzdem immer genau zur richtigen Zeit ausgelöst, selbst wenn sie erst vor einer Minute ausgelöst wurde? Ist es nur, weil es eine System-App ist? Was ist, wenn es als Benutzer-App auf einem Gerät installiert wird, auf dem es nicht integriert ist?
Wenn Sie sagen , dass es ist , weil es sich um eine System - App ist, habe ich eine andere App gefunden , die einen Alarm zweimal in 2 Minuten auslösen können, hier , obwohl ich denke , es manchmal ein Vordergrund Dienst verwenden können.
EDIT: eine winzige Github - Repository , um zu versuchen Ideen auf, machte hier .
BEARBEITEN: Endlich ein Beispiel gefunden , das sowohl Open-Source ist als auch dieses Problem nicht hat. Leider ist es sehr komplex und ich versuche immer noch herauszufinden, was es so anders macht (und welchen minimalen Code ich meinem POC hinzufügen sollte), damit die Alarme nach dem Entfernen der App aus den letzten Aufgaben geplant bleiben
quelle
Antworten:
Wir haben nichts zu tun.
Sobald Ihre App nicht auf der Whitelist steht, wird sie immer beendet, sobald sie aus den letzten Apps entfernt wurde.
Weil Original Equipment Manufacturer (OMEs) ständig gegen die Android-Konformität verstoßen .
Wenn Ihre App nicht von der Geräteherstellung in die Whitelist aufgenommen wurde, werden keine Hintergrundarbeiten ausgelöst, auch keine Alarme - falls Ihre App aus den letzten Apps entfernt wird.
Eine Liste der Geräte mit diesem Verhalten finden Sie hier. AUCH möglicherweise finden Sie eine Nebenlösung. Sie funktioniert jedoch nicht gut.
quelle
Es wurde eine seltsame Problemumgehung gefunden (Beispiel hier ), die für alle Versionen zu funktionieren scheint, einschließlich sogar für Android R:
Auf Android R muss es auch gewährt werden. Vorher scheint es nicht so, als müsste es gewährt, nur deklariert werden. Ich bin mir nicht sicher, warum sich dies bei R geändert hat, aber ich kann sagen, dass SAW als mögliche Lösung erforderlich sein könnte, um Dinge im Hintergrund zu starten, wie hier für Android 10 geschrieben.
FakeActivity.kt
Sie können diese Aktivität mit diesem Thema auch für den Benutzer fast unsichtbar machen:
Leider ist dies eine seltsame Problemumgehung. Ich hoffe, eine bessere Lösung dafür zu finden.
Die Einschränkung bezieht sich auf das Starten von Aktivität. Daher ist meine aktuelle Idee, dass es möglicherweise auch hilfreich ist, wenn ich einen Vordergrunddienst für den Bruchteil einer Sekunde starte, und dafür benötige ich nicht einmal die SAW-Berechtigung.
EDIT: OK Ich habe es mit einem Vordergrunddienst versucht (Beispiel hier ), und es hat nicht funktioniert. Keine Ahnung, warum eine Aktivität funktioniert, aber kein Dienst. Ich habe sogar versucht, den Alarm dort neu zu planen, und versucht, den Service auch nach einer Neuplanung für eine Weile stehen zu lassen. Ich habe auch einen normalen Dienst ausprobiert, aber natürlich wurde er sofort geschlossen, da die Aufgabe entfernt wurde und überhaupt nicht funktionierte (selbst wenn ich einen Thread erstellt habe, der im Hintergrund ausgeführt werden soll).
Eine andere mögliche Lösung, die ich nicht ausprobiert habe, besteht darin, für immer einen Vordergrunddienst zu haben, oder zumindest bis die Aufgabe entfernt wurde. Dies ist jedoch etwas seltsam und ich sehe die Apps, die ich erwähnt habe, nicht.
BEARBEITEN: Es wurde versucht, einen Vordergrunddienst auszuführen, bevor die Aufgabe der App entfernt wurde, und einige Zeit danach, und der Alarm funktionierte immer noch. Es wurde auch versucht, diesen Dienst als Verantwortlichen für das Entfernen von Aufgaben zu verwenden und sich sofort zu schließen, wenn er auftritt, und er funktionierte immer noch (Beispiel hier ). Der Vorteil dieser Problemumgehung besteht darin, dass Sie überhaupt nicht über die SAW-Berechtigung verfügen müssen. Der Nachteil ist, dass Sie einen Dienst mit einer Benachrichtigung haben, während die App für den Benutzer bereits sichtbar ist. Ich frage mich, ob es möglich ist, die Benachrichtigung auszublenden, während die App über die Aktivität bereits im Vordergrund steht.
BEARBEITEN: Scheint ein Fehler in Android Studio zu sein ( hier gemeldet , einschließlich Videos, in denen Versionen verglichen werden). Wenn Sie die App von der problematischen Version aus starten, die ich versucht habe, können die Alarme gelöscht werden.
Wenn Sie die App über den Launcher starten, funktioniert sie einwandfrei.
Dies ist der aktuelle Code zum Einstellen des Alarms:
Ich muss nicht einmal "pendingShowList" verwenden. Die Verwendung von null ist ebenfalls in Ordnung.
quelle
SYSTEM_ALERT_WINDOW
Erlaubnis?Intent.FLAG_RECEIVER_FOREGROUND
Flag hat.https://developer.android.com/about/versions/oreo/background#broadcasts
setExactAndAllowWhileIdle()
wenn API Targeting 23+.https://developer.android.com/about/versions/oreo/background#migration
quelle
Ich weiß, dass dies nicht effizient ist, aber mit einer Genauigkeit von 60 Sekunden konsistenter sein könnte.
https://developer.android.com/reference/android/content/Intent#ACTION_TIME_TICK
Wenn dieser Rundfunkempfänger in einem Vordergrunddienst verwendet wird, können Sie die Uhrzeit jede Minute überprüfen und eine Entscheidung über eine Aktion treffen.
quelle
Ich denke, Sie können den Benutzer bitten, die Berechtigung so festzulegen, dass der Energiesparmodus deaktiviert wird, und den Benutzer warnen, dass genaue Zeiten nicht erreicht werden, wenn er sie nicht verwendet.
Hier ist der Code, um ihn anzufordern:
quelle
Ich bin der Autor des Open-Source-Projekts, das Sie in Ihrer Frage erwähnt haben ( einfacher Wecker) .
Ich bin überrascht, dass die Verwendung von AlarmManager.setAlarmClock bei Ihnen nicht funktioniert hat, da meine App genau das tut. Der Code befindet sich in der Datei AlarmSetter.kt. Hier ist ein Ausschnitt:
Grundsätzlich ist es nichts Besonderes, stellen Sie nur sicher, dass Intent eine Aktion und eine Zielklasse hat, die in meinem Fall ein Rundfunkempfänger ist.
quelle