Uwaga: Wypróbowałem różne rozwiązania, o których pisano tutaj na StackOverflow (przykład tutaj ). Nie zamykaj tego, nie sprawdzając, czy twoje rozwiązanie z tego, co znalazłeś, działa przy użyciu testu, który napisałem poniżej.
tło
W aplikacji jest wymagane, aby użytkownik ustawił przypomnienie, które ma być zaplanowane o określonej godzinie, więc gdy aplikacja zostanie uruchomiona w tym czasie, robi coś małego w tle (tylko niektóre operacje zapytania DB) i pokazuje proste powiadomienie, aby opowiedzieć o przypomnieniu.
W przeszłości korzystałem z prostego kodu, aby ustawić coś do zaplanowania we względnie określonym czasie:
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
}
}
Stosowanie:
val timeToTrigger = System.currentTimeMillis() + java.util.concurrent.TimeUnit.MINUTES.toMillis(1)
setAlarm(this, timeToTrigger, 1)
Problem
Testowałem teraz ten kod na emulatorach na nowych wersjach Androida i na Pixelu 4 z Androidem 10 i wydaje się, że nie uruchamia się, a może uruchamia się po bardzo długim czasie od czasu, gdy go dostarczam. Zdaję sobie sprawę z okropnego zachowania, które niektórzy producenci OEM dodali do usuwania aplikacji z ostatnich zadań, ale ten dotyczy zarówno emulatorów, jak i urządzenia Pixel 4 (w magazynie).
Przeczytałem o dokumentach dotyczących ustawiania alarmu, że został on ograniczony do aplikacji, aby nie pojawiał się zbyt często, ale to nie wyjaśnia, jak ustawić alarm w określonym czasie i nie wyjaśnia jak to się dzieje, że aplikacja Google Clock osiąga sukces?
Nie tylko to, ale zgodnie z tym, co rozumiem, mówi, że ograniczenia powinny być stosowane szczególnie w przypadku stanu niskiego poboru mocy urządzenia, ale w moim przypadku nie miałem tego stanu, zarówno na urządzeniu, jak i na emulatorach. Ustawiłem uruchamianie alarmów za około minutę.
Widząc, że wiele aplikacji budzika nie działa już tak, jak kiedyś, myślę, że czegoś brakuje w dokumentacji. Przykładem takich aplikacji jest popularna aplikacja Timely, która została kupiona przez Google, ale nigdy nie otrzymała nowych aktualizacji w celu obsługi nowych ograniczeń, a teraz użytkownicy chcą ją z powrotem. . Jednak niektóre popularne aplikacje działają dobrze, takie jak ta .
Co próbowałem
Aby sprawdzić, czy rzeczywiście działa alarm, wykonuję te testy, gdy próbuję wyzwolić alarm za minutę po zainstalowaniu aplikacji po raz pierwszy, wszystko po podłączeniu urządzenia do komputera (aby wyświetlić dzienniki):
- Testuj, gdy aplikacja jest na pierwszym planie, widoczna dla użytkownika. - zajęło 1-2 minuty.
- Sprawdź, kiedy aplikacja została wysłana w tło (na przykład za pomocą przycisku Home) - zajęło to około 1 minuty
- Sprawdź, kiedy zadanie aplikacji zostało usunięte z ostatnich zadań. - Czekałem ponad 20 minut i nie widziałem wyzwolenia alarmu, piszącego do dzienników.
- Jak # 3, ale także wyłącz ekran. Prawdopodobnie byłoby gorzej ...
Próbowałem użyć następnych rzeczy, wszystko nie działa:
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
połączenie któregokolwiek z powyższych, z:
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) alarmManager.setWindow(AlarmManager.RTC_WAKEUP, 0, 60 * 1000L, pendingIntent)
Próbowałem użyć usługi zamiast BroadcastReceiver. Wypróbowałem też inny proces.
Próbowałem zignorować aplikację z optymalizacji baterii (nie pomogło), ale ponieważ inne aplikacje jej nie potrzebują, ja też nie powinienem jej używać.
Próbowałem użyć tego:
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP)
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
- Próbowałem mieć usługę, która będzie wyzwalana przez onTaskRemoved , aby ponownie ustawić tam alarm, ale to też nie pomogło (usługa działała dobrze).
Jeśli chodzi o aplikację Google Clock, nie widziałem w niej nic specjalnego poza tym, że wyświetla powiadomienie przed uruchomieniem, a także nie widzę go w sekcji „niezoptymalizowanej” ekranu ustawień optymalizacji baterii.
Widząc, że to wygląda na błąd, zgłosiłem to tutaj , w tym przykładowy projekt i wideo pokazujące problem.
Sprawdziłem wiele wersji emulatora i wygląda na to, że takie zachowanie zaczęło się od API 27 (Android 8.1 - Oreo). Patrząc na dokumenty , nie widzę wzmianki o AlarmManager, ale napisano o różnych pracach w tle.
Pytania
Jak ustawić coś, co ma być teraz uruchamiane we względnie dokładnym czasie?
Dlaczego powyższe rozwiązania już nie działają? Czy coś mi brakuje? Pozwolenie? Może powinienem zamiast tego użyć Robotnika? Ale czy to nie znaczy, że może wcale nie zadziałać na czas?
W jaki sposób aplikacja Google „Zegar” przezwycięża to wszystko i zawsze uruchamia się dokładnie o określonej godzinie, nawet jeśli została uruchomiona jeszcze minutę temu? Czy tylko dlatego, że jest to aplikacja systemowa? Co się stanie, jeśli zostanie zainstalowany jako aplikacja użytkownika na urządzeniu, które nie ma wbudowanej aplikacji?
Jeśli powiesz, że to dlatego, że jest to aplikacja systemowa, Znalazłem inną aplikację, która może wywołać alarm dwukrotnie w ciągu 2 minut, tutaj , choć myślę, że może skorzystać z usługi pierwszoplanowy czasami.
EDYCJA: utworzyłem małe repozytorium Github do przetestowania pomysłów tutaj .
EDYCJA: w końcu znalazłem próbkę, która jest zarówno otwarta, jak i nie ma tego problemu. Niestety jest to bardzo skomplikowane i wciąż próbuję dowiedzieć się, co czyni go tak różnym (i jaki jest minimalny kod, który powinienem dodać do mojego POC), który pozwala, aby jego alarmy pozostały zaplanowane po usunięciu aplikacji z ostatnich zadań
źródło
Odpowiedzi:
Nie mamy nic do roboty.
Gdy aplikacja nie znajduje się na białej liście, zawsze zostanie zabita po usunięciu z najnowszych aplikacji.
Ponieważ producent oryginalnego sprzętu (OME) stale narusza zgodność z Androidem .
Więc jeśli twoja aplikacja nie znajduje się na białej liście od urządzenia Wyprodukuj, nie uruchomi żadnej pracy w tle, nawet alarmów - w przypadku, gdy Twoja aplikacja zostanie usunięta z najnowszych aplikacji.
Możesz znaleźć listę urządzeń o takim zachowaniu tutaj TAKŻE możesz znaleźć rozwiązanie poboczne, jednak to nie zadziała dobrze.
źródło
Znaleziono dziwne obejście (przykład tutaj ), które wydaje się działać we wszystkich wersjach, w tym nawet na Androida R:
W Androidzie R trzeba będzie również to przyznać. Wcześniej nie wydaje się, że należy to przyznać, po prostu zadeklarować. Nie jestem pewien, dlaczego zmieniło się to na R, ale mogę powiedzieć, że SAW może być wymagane jako możliwe rozwiązanie do uruchamiania w tle, jak napisano tutaj dla Androida 10.
FakeActivity.kt
Możesz również uczynić tę aktywność prawie niewidoczną dla użytkownika za pomocą tego motywu:
Niestety jest to dziwne obejście. Mam nadzieję znaleźć lepsze rozwiązanie tego problemu.
Ograniczenie mówi o rozpoczęciu działania, więc moim obecnym pomysłem jest to, że jeśli uruchomię usługę pierwszoplanową na ułamek sekundy, to również pomoże, a do tego nie potrzebuję nawet zgody SAW.
EDYCJA: OK Próbowałem z usługą pierwszego planu (próbka tutaj ) i to nie działało. Nie mam pojęcia, dlaczego działanie działa, ale nie usługa. Próbowałem nawet zmienić harmonogram alarmu i starałem się, aby usługa pozostała przez jakiś czas, nawet po ponownym harmonogramie. Próbowałem także normalnej usługi, ale oczywiście została ona natychmiast zamknięta, ponieważ zadanie zostało usunięte i w ogóle nie działało (nawet jeśli utworzyłem wątek do uruchomienia w tle).
Innym możliwym rozwiązaniem, którego nie próbowałem, jest posiadanie usługi pierwszoplanowej na zawsze, a przynajmniej do momentu usunięcia zadania, ale jest to trochę dziwne i nie widzę aplikacji, o których wspominałem.
EDYCJA: próbowałem uruchomić usługę pierwszoplanową przed usunięciem zadania aplikacji i jeszcze chwilę później, a alarm nadal działał. Próbowałem także, aby ta usługa była odpowiedzialna za zdarzenie usuwające zadanie i aby zamknęła się od razu, gdy wystąpi, i nadal działało (przykład tutaj ). Zaletą tego obejścia jest to, że wcale nie musisz mieć uprawnienia SAW. Wadą jest to, że masz usługę z powiadomieniem, gdy aplikacja jest już widoczna dla użytkownika. Zastanawiam się, czy można ukryć powiadomienie, gdy aplikacja jest już na pierwszym planie za pośrednictwem działania.
EDYCJA: Wydaje się, że jest to błąd w Android Studio (zgłaszany tutaj , w tym filmy porównujące wersje). Po uruchomieniu aplikacji z problematycznej wersji, którą próbowałem, może to spowodować skasowanie alarmów.
Jeśli uruchomisz aplikację z poziomu programu uruchamiającego, działa dobrze.
To jest aktualny kod do ustawienia alarmu:
Nie muszę nawet używać „pendingShowList”. Używanie wartości null jest również w porządku.
źródło
SYSTEM_ALERT_WINDOW
zgody?Intent.FLAG_RECEIVER_FOREGROUND
flagę.https://developer.android.com/about/versions/oreo/background#broadcasts
setExactAndAllowWhileIdle()
kierowania na interfejs API 23+.https://developer.android.com/about/versions/oreo/background#migration
źródło
Wiem, że to nie jest wydajne, ale może być bardziej spójne z dokładnością 60 sekund.
https://developer.android.com/reference/android/content/Intent#ACTION_TIME_TICK
jeśli ten odbiornik rozgłoszeniowy jest używany w ramach usługi pierwszego planu, możesz sprawdzać godzinę co minutę i podejmować decyzję o podjęciu działania.
źródło
Myślę, że możesz poprosić użytkownika o ustawienie uprawnienia, aby wyłączało tryb oszczędzania energii i ostrzec użytkownika, że jeśli go nie użyje, dokładne czasy nie zostaną osiągnięte.
Oto kod, aby o to poprosić:
źródło
Jestem autorem projektu open source, o którym wspomniałeś w swoim pytaniu ( prosty budzik) .
Dziwi mnie, że użycie AlarmManager.setAlarmClock nie zadziałało, ponieważ moja aplikacja właśnie to robi. Kod znajduje się w pliku AlarmSetter.kt. Oto fragment:
Zasadniczo nie jest to nic specjalnego, tylko upewnij się, że zamiar ma akcję i klasę docelową, która w moim przypadku jest odbiornikiem transmisji.
źródło