Android 11 (poziom API 30) lub nowszy obsługuje zamrażanie aplikacji w pamięci podręcznej. Ta funkcja zatrzymuje wykonywanie procesów z pamięci podręcznej i ogranicza wykorzystanie zasobów przez nieprawidłowo działające aplikacje, które mogą próbować działać w pamięci podręcznej.
Zamrażarka aplikacji w pamięci podręcznej utrzymuje aplikacje w pamięci RAM, ale nie obciąża procesora. Jeśli Android uzna, że aplikacja nie powinna działać, ale może być potrzebna w przyszłości, zamraża jej proces, zamiast go kończyć. Zapobiega to uruchomieniu „na zimno”, gdy aplikacja będzie ponownie potrzebna.
Android zamraża aplikacje w pamięci podręcznej, przenosząc ich procesy do zamrożonej grupy cgroup. Zmniejsza to zużycie procesora w trybie aktywnym i bezczynnym w przypadku aktywnych aplikacji w pamięci podręcznej. Możesz włączyć zamrażanie aplikacji za pomocą flagi konfiguracji systemu lub opcji programisty.
W Androidzie 14 (API na poziomie 34) i nowszych wersjach zamrażarka aplikacji w pamięci podręcznej ma te zaawansowane funkcje:
- Procesy aplikacji w stanie buforowanym są zamrażane 10 sekund po przejściu w ten stan.
- System natychmiast odblokowuje zamrożony proces aplikacji podczas zdarzenia związanego z cyklem życia. Obejmują one otrzymanie intencji, rozpoczęcie usługi zadania lub wznowienie aktywności przez użytkownika.
ActivityManagerService zarządza wszystkimi procesami aplikacji i podejmuje decyzje dotyczące cyklu życia aplikacji. CachedAppOptimizer odpowiada za zamrażanie procesu aplikacji.
Gdy proces aplikacji jest zamrożony, wszystkie jego wątki są zawieszone i nie mogą wykonywać pracy procesora, dopóki nie zostaną odblokowane. W związku z tym aplikacja nie może przeprowadzać odzyskiwania pamięci ani odpowiadać na zdarzenia związane z przycinaniem pamięci. Więcej informacji znajdziesz w sekcji ComponentCallbacks2.onTrimMemory(int). Aby to uwzględnić, od Androida 14:
- Aplikacje z widoczną instancją
Activitysą powiadamiane oTRIM_MEMORY_UI_HIDDEN, gdy tylko przejdą w tle. Aplikacje, które pozostają w cyklu życia bez interfejsu, np. aplikacje z usługą działającą na pierwszym planie, mogą otrzymaćTRIM_MEMORY_BACKGROUND. Inne zdarzenia przycinania nie są dostarczane, ponieważ gdy aplikacje kwalifikują się do tych zdarzeń, oczekuje się, że będą zamrożone. - Krótko po przejściu do stanu buforowania system może poprosić środowisko wykonawcze aplikacji o przeprowadzenie odzyskiwania pamięci w ramach przygotowań do potencjalnego zamrożenia.
- Gdy proces aplikacji jest zamrożony, mogą wystąpić dodatkowe kroki kompresji pamięci, takie jak zapisywanie zmodyfikowanych stron w pamięci zapasowej i przenoszenie anonimowych stron do ZRAM.
- Jeśli wszystkie procesy danej aplikacji są zamrożone, system zamyka wszystkie aktywne gniazda TCP utrzymywane przez aplikację. Zapobiega to wysyłaniu przez serwer pingów TCP keepalive, które mogłyby wybudzić modem urządzenia.
Procesy aplikacji z pamięci podręcznej są odblokowywane, gdy ich stan zmienia się z „cached” na stan o większym znaczeniu. Aby ograniczyć liczbę zdarzeń odblokowania w Androidzie 14 i nowszych wersjach, system umieszcza w kolejce transmisje zarejestrowane w kontekście, gdy aplikacja jest w stanie buforowania. Kontekstowo zarejestrowane transmisje to odbiorniki, które aplikacja rejestruje dynamicznie, wywołując funkcję Context.registerReceiver. System dostarcza te kolejkowane transmisje dopiero po odblokowaniu aplikacji. W przeciwieństwie do tego system nie umieszcza w kolejce transmisji zadeklarowanych w pliku manifestu.
Komunikaty zadeklarowane w pliku manifestu to odbiorniki zadeklarowane statycznie w AndroidManifest.xml za pomocą elementu <receiver>. System natychmiast odblokowuje aplikację w pamięci podręcznej, aby dostarczać transmisje zadeklarowane w pliku manifestu.
Wpływ na stan systemu
Android zamyka najrzadziej używany proces aplikacji w pamięci podręcznej, jeśli jest ich więcej niż MAX_CACHED_PROCESSES. Na obsługiwanych urządzeniach z Androidem 14 lub nowszym wartość MAX_CACHED_PROCESSES jest znacznie większa, co pozwala urządzeniom przechowywać w pamięci RAM znacznie więcej procesów aplikacji w pamięci podręcznej.
Przechowywanie większej liczby aplikacji w pamięci RAM może zmniejszyć liczbę uruchomień „na zimno” nawet o 30%, a wartość ta zależy od całkowitej ilości pamięci RAM urządzenia. Jednocześnie zużycie procesora przez aplikacje w pamięci podręcznej jest minimalizowane, co znacznie oszczędza baterię.
Wyjątki dotyczące zamrażarek
W określonych warunkach proces aplikacji może przejść w stan buforowania, ale pozostać w stanie odblokowanym. Te wyjątki są szczegółami implementacji i mogą się zmienić w przyszłych wersjach Androida:
- Blokady plików: jeśli proces w pamięci podręcznej ma blokadę pliku, która blokuje inne procesy spoza pamięci podręcznej, proces z blokadą nie jest zamrażany.
BIND_WAIVE_PRIORITYpowiązania: procesy aplikacji z powiązaniami przychodzącymi utworzonymi za pomocąContext.BIND_WAIVE_PRIORITYmogą przejść w stan buforowany, ale pozostaną niezamrożone, dopóki wszystkie połączone procesy klienta również nie zostaną buforowane. To wyłączenie obsługuje aplikacje wieloprocesowe, takie jak przeglądarki internetowe korzystające z niestandardowych kart.
Wdrażanie zamrażarki aplikacji
Funkcja zamrażania buforowanych aplikacji korzysta z zamrażarki cgroup v2 jądra. Można go włączyć na urządzeniach dostarczanych z kompatybilnym jądrem. Włącz opcję dla programistów Zawieszanie wykonywania w przypadku aplikacji w pamięci podręcznej lub ustaw flagę konfiguracji urządzenia activity_manager_native_boot use_freezer na true. Przykład:
adb shell device_config put activity_manager_native_boot use_freezer true && adb rebootZamrażanie jest wyłączone, gdy ustawisz flagę use_freezer na false lub wyłączysz opcję dla programistów. Przykład:
adb shell device_config put activity_manager_native_boot use_freezer false && adb rebootTo ustawienie możesz włączyć lub wyłączyć, zmieniając konfigurację urządzenia w wersji lub aktualizacji oprogramowania.
Aby zastąpić wartość MAX_CACHED_PROCESSES, np. ustawić wartość 1024 na potrzeby testowania:
adb shell device_config put activity_manager max_cached_processes 1024
adb shell device_config set_sync_disabled_for_tests persistentAby cofnąć zmianę MAX_CACHED_PROCESSES:
adb shell device_config delete activity_manager max_cached_processes
adb shell device_config set_sync_disabled_for_tests noneZamrażarka aplikacji nie udostępnia oficjalnych interfejsów API i nie ma klienta implementacji referencyjnej, ale korzysta z ukrytych interfejsów API systemu setProcessFrozen, aby zamrozić poszczególne procesy, oraz enableFreezer, aby włączyć lub wyłączyć zamrażanie w całym systemie.
Obsługa funkcji niestandardowych
Procesy aplikacji nie powinny wykonywać żadnych działań, gdy są w pamięci podręcznej, ale niektóre aplikacje mogą mieć niestandardowe funkcje obsługiwane przez procesy, które powinny działać, gdy są w pamięci podręcznej. Gdy na urządzeniu z takimi aplikacjami włączona jest funkcja zamrażania aplikacji, procesy w pamięci podręcznej są zamrażane i mogą uniemożliwiać działanie funkcji niestandardowych.
Aby obejść ten problem, możesz zmienić stan procesu na niebuforowany, zanim proces będzie musiał wykonać jakąkolwiek pracę. Dzięki tej zmianie aplikacje pozostaną aktywne. Przykłady aktywnych stanów to powiązana usługa na pierwszym planie lub stan na pierwszym planie.
Typowe rodzaje błędów
Gdy procesy aplikacji są zamrożone, nieprawidłowa komunikacja międzyprocesowa (IPC) lub planowanie zadań może prowadzić do zamykania aplikacji lub nieoczekiwanego działania.
Synchroniczne transakcje w binderze do zamrożonych procesów
Gdy proces aplikacji klienckiej wyśle synchroniczną transakcję powiązania do zamrożonego procesu aplikacji serwera, system natychmiast zakończy ten proces. Zapobiega to blokowaniu wątku klienta na czas nieokreślony podczas oczekiwania na odpowiedź z zamrożonego serwera. Wątek klienta otrzymuje wtedy RemoteException, a wszyscy zarejestrowani odbiorcy są wywoływani. Więcej informacji znajdziesz w sekcji IBinder.linkToDeath.
Przyczyna: ten błąd jest zwykle spowodowany błędem w aplikacji klienta.
Gdy klient wiąże się z usługą, proces serwera jest powiązany z klientem i nie może przejść do stanu buforowanego przed klientem. Więcej informacji znajdziesz w sekcji Context.bindService. Gdy jednak klient wywoła Context.unbindService, proces serwera może zostać zapisany w pamięci podręcznej i zamrożony. Jeśli klient nadal używa buforowanego odwołania po odłączeniu, istnieje ryzyko, że będzie się komunikować z zamrożonym procesem.IBinder
Aby uniknąć tego problemu, upewnij się, że aplikacje klienckie odrzucają odwołania IBinder natychmiast po wywołaniu funkcji Context.unbindService.
Przepełnienie bufora transakcji asynchronicznej zszywarki
Gdy proces aplikacji serwera odbiera asynchroniczne (oneway) transakcje binderów w stanie zamrożenia, transakcje są buforowane w buforze dla każdego procesu. Jeśli serwer otrzyma zbyt wiele transakcji asynchronicznych w czasie, gdy jest zamrożony, bufor przepełni się, a system zakończy proces aplikacji serwera.
Aby zapobiec przepełnieniu bufora, unikaj wysyłania zbyt wielu asynchronicznych transakcji binder do procesów, które mogą być buforowane lub zamrażane.
Wielokrotne wykonywanie zaplanowanych zadań po odblokowaniu
Jeśli aplikacja wykonuje powtarzalne zadania, są one wstrzymywane, gdy proces jest zamrożony. Więcej informacji znajdziesz w sekcji ScheduledThreadPoolExecutor.scheduleAtFixedRate lub Timer.scheduleAtFixedRate. Gdy proces zostanie odblokowany, zgromadzone pominięte wykonania mogą zostać uruchomione szybko, jeden po drugim, praktycznie bez opóźnienia.
Aby zapobiec nagłemu wzrostowi liczby wykonań po odblokowaniu aplikacji, używaj scheduleWithFixedDelay zamiast scheduleAtFixedRate w przypadku zadań w tle. Możesz też użyć WorkManager.
Testowanie i rozwiązywanie problemów z zamrażarką aplikacji
Aby sprawdzić, czy zamrażarka aplikacji działa zgodnie z oczekiwaniami, lub rozwiązać problemy z nią związane, użyj tych narzędzi diagnostycznych i poleceń:
Polecenia menedżera aktywności
Za pomocą poleceń adb shell am możesz ręcznie sterować zamrażaniem i kompresją w przypadku konkretnego procesu:
Wymuszanie zamrożenia procesu:
adb shell am freeze <process>Wymuś odblokowanie procesu:
adb shell am unfreeze <process>Wymuś pełną kompresję pamięci w procesie:
adb shell am compact full <process>
Sprawdzanie logcat
Wyświetl logcat, aby zobaczyć zamrożone i odmrożone wpisy za każdym razem, gdy proces migruje do lub z zamrażarki:
adb logcat | grep -i "\(freezing\|froze\)"Wartości wyliczeniowe danych wyjściowych logów przyczyny odblokowania z wyliczenia bufora protokołu UnfreezeReason.
Sprawdzanie za pomocą narzędzia dumpsys
Sprawdź listę zamrożonych procesów za pomocą polecenia dumpsys activity:
adb shell dumpsys activity | grep -A 20 "Apps frozen:"Sprawdź, czy plik /sys/fs/cgroup/uid_0/cgroup.freeze jest obecny.
ApplicationExitInfo
Aby wysłać zapytanie o powód wcześniejszego zakończenia procesu, zapoznaj się z tym artykułem:ActivityManager.getHistoricalProcessExitReasons
Jeśli proces aplikacji został zakończony z powodu problemu związanego z zamrożeniem, np. otrzymania synchronicznej transakcji bindera podczas zamrożenia, jako przyczynę zakończenia ustawia się ApplicationExitInfo.REASON_FREEZER.
Śledzenie Perfetto
Zdarzenia związane z zamrażaniem są emitowane na ścieżkę o nazwie Freezer w ramach procesu system_server w śladach Perfetto:
- Wycinki
FreezeiUnfreezewskazują, kiedy proces zmienia stan. - Zdarzenia
updateAppFreezeStateLSPpokazują, kiedy serwer systemu ponownie sprawdza atrybuty procesu, aby podjąć decyzję o zamrożeniu lub odmrożeniu.
Możesz sprawdzić te zdarzenia bezpośrednio w interfejsie Perfetto lub przeanalizować je za pomocą PerfettoSQL:
INCLUDE PERFETTO MODULE slices.with_context;
SELECT *
FROM process_slice
WHERE process_name = "system_server"
AND track_name = "Freezer"
AND (name LIKE "Freeze %" OR name LIKE "Unfreeze %");
W standardowej bibliotece PerfettoSQL zdarzenia zamrażania są też podsumowywane w tabeli android_freezer_events.