Podczas korzystania z bindera do komunikacji między procesami zachowaj szczególną ostrożność, gdy zdalny proces jest w stanie buforowanym lub zamrożonym. Wywołania w przypadku aplikacji w pamięci podręcznej lub zamrożonych mogą powodować ich awarię lub niepotrzebne zużycie zasobów.
Stany aplikacji w pamięci podręcznej i zamrożone
Android utrzymuje aplikacje w różnych stanach, aby zarządzać zasobami systemowymi, takimi jak pamięć i procesor.
Stan buforowany
Gdy aplikacja nie ma widocznych dla użytkownika komponentów, takich jak aktywności lub usługi, może zostać przeniesiona do stanu buforowania. Więcej informacji znajdziesz w artykule Procesy i cykl życia aplikacji. Aplikacje w pamięci podręcznej są przechowywane w pamięci na wypadek, gdyby użytkownik chciał do nich wrócić, ale nie powinny aktywnie działać.
Podczas wiązania z jednego procesu aplikacji z innym, np. za pomocą bindService, stan procesu serwera jest podnoszony do poziomu co najmniej tak ważnego jak proces klienta (z wyjątkiem sytuacji, gdy określono Context#BIND_WAIVE_PRIORITY). Jeśli np. klient nie jest w stanie buforowanym, serwer też nie jest.
Z kolei stan procesu serwera nie określa stanu jego klientów. Serwer może więc mieć połączenia z klientami, najczęściej w postaci wywołań zwrotnych, a gdy proces zdalny jest w stanie buforowanym, serwer nie jest buforowany.
Podczas projektowania interfejsów API, w których wywołania zwrotne pochodzą z procesu o podwyższonych uprawnieniach i są dostarczane do aplikacji, rozważ wstrzymanie wysyłania wywołań zwrotnych, gdy aplikacja przechodzi w stan buforowany, i wznowienie ich wysyłania, gdy opuszcza ten stan. Zapobiega to niepotrzebnej pracy w procesach aplikacji z pamięci podręcznej.
Aby śledzić, kiedy aplikacje wchodzą w stan buforowania lub go opuszczają, użyj tych funkcji:ActivityManager.addOnUidImportanceListener
// in ActivityManager or Context
activityManager.addOnUidImportanceListener(
new UidImportanceListener() { ... },
IMPORTANCE_CACHED);
Stan zawieszenia
System może zamrozić aplikację w pamięci podręcznej, aby oszczędzać zasoby. Gdy aplikacja jest zamrożona, nie otrzymuje czasu procesora i nie może wykonywać żadnych działań. Więcej informacji znajdziesz w artykule Zamrażanie aplikacji w pamięci podręcznej.
Gdy proces wysyła synchroniczną (nie oneway) transakcję bindera do innego zamrożonego procesu zdalnego, system zamyka ten proces zdalny. Zapobiega to zawieszeniu wątku wywołującego w procesie wywołującym na czas nieokreślony podczas oczekiwania na odmrożenie procesu zdalnego, co mogłoby spowodować brak zasobów wątku lub zakleszczenia w aplikacji wywołującej.
Gdy proces wysyła asynchroniczną (oneway) transakcję powiązania do zamrożonej aplikacji (zwykle przez powiadomienie wywołania zwrotnego, które jest zazwyczaj metodą oneway), transakcja jest buforowana do czasu odmrożenia procesu zdalnego. Jeśli bufor przepełni się, proces aplikacji odbiorcy może ulec awarii. Dodatkowo transakcje buforowane mogą się zdezaktualizować, zanim proces aplikacji zostanie odmrożony i je przetworzy.
Aby uniknąć przepełnienia aplikacji nieaktualnymi zdarzeniami lub przepełnienia ich buforów, musisz wstrzymać wysyłanie wywołań zwrotnych, gdy proces aplikacji odbiorcy jest zamrożony.
Aby śledzić, kiedy aplikacje są zamrażane lub odmrażane, użyj tego kodu:IBinder.addFrozenStateChangeCallback
// The binder token of the remote process
IBinder binder = service.getBinder();
// Keep track of frozen state
AtomicBoolean remoteFrozen = new AtomicBoolean(false);
// Update remoteFrozen when the remote process freezes or unfreezes
binder.addFrozenStateChangeCallback(
myExecutor,
new IBinder.FrozenStateChangeCallback() {
@Override
public void onFrozenStateChanged(boolean isFrozen) {
remoteFrozen.set(isFrozen);
}
});
// When dispatching callbacks to the remote process, pause dispatch if frozen:
if (!remoteFrozen.get()) {
// dispatch callback to remote process
}
Używanie RemoteCallbackList
Klasa RemoteCallbackList pomaga zarządzać listami wywołań zwrotnych IInterface rejestrowanych przez procesy zdalne. Ta klasa automatycznie obsługuje powiadomienia o zakończeniu działania usługi Binder i zapewnia opcje obsługi wywołań zwrotnych do zamrożonych aplikacji.
Podczas tworzenia RemoteCallbackList możesz określić zasadę zamrożonego wywołującego:
FROZEN_CALLEE_POLICY_DROP: wywołania zwrotne do zamrożonych aplikacji są cicho odrzucane. Używaj tej zasady, gdy zdarzenia, które miały miejsce podczas buforowania aplikacji, nie są dla niej ważne, np. zdarzenia z czujników w czasie rzeczywistym.FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT: Jeśli podczas zamrożenia aplikacji zostanie wyemitowanych wiele wywołań zwrotnych, do kolejki zostanie dodane tylko najnowsze z nich i zostanie ono dostarczone po odmrożeniu aplikacji. Jest to przydatne w przypadku wywołań zwrotnych opartych na stanie, w których liczy się tylko najnowsza aktualizacja stanu, np. wywołanie zwrotne powiadamiające aplikację o bieżącej głośności multimediów.FROZEN_CALLEE_POLICY_ENQUEUE_ALL: wszystkie wywołania zwrotne emitowane, gdy aplikacja jest zamrożona, są umieszczane w kolejce i dostarczane po jej odmrożeniu. Zachowaj ostrożność w przypadku tej zasady, ponieważ może ona prowadzić do przepełnienia bufora, jeśli zostanie umieszczonych w kolejce zbyt wiele wywołań zwrotnych, lub do gromadzenia się nieaktualnych zdarzeń.
Poniższy przykład pokazuje, jak utworzyć i użyć instancji RemoteCallbackList, która odrzuca wywołania zwrotne do zamrożonych aplikacji:
RemoteCallbackList<IMyCallbackInterface> callbacks =
new RemoteCallbackList.Builder<IMyCallbackInterface>(
RemoteCallbackList.FROZEN_CALLEE_POLICY_DROP)
.setExecutor(myExecutor)
.build();
// Registering a callback:
callbacks.register(callback);
// Broadcasting to all registered callbacks:
callbacks.broadcast((callback) -> callback.onSomeEvent(eventData));
Jeśli używasz FROZEN_CALLEE_POLICY_DROP, system wywołuje callback.onSomeEvent() tylko wtedy, gdy proces hostujący wywołanie zwrotne nie jest zamrożony.
usługi systemowe i interakcje z aplikacjami;
Usługi systemowe często wchodzą w interakcje z wieloma różnymi aplikacjami za pomocą mechanizmu Binder. Ponieważ aplikacje mogą przechodzić w stan buforowania i zamrożenia, usługi systemowe muszą zachowywać szczególną ostrożność, aby prawidłowo obsługiwać te interakcje i utrzymywać stabilność oraz wydajność systemu.
Usługi systemowe muszą już obsługiwać sytuacje, w których procesy aplikacji są zamykane z różnych powodów. Obejmuje to zaprzestanie pracy w ich imieniu i niepodejmowanie prób dalszego dostarczania wywołań zwrotnych do martwych procesów. Rozważenie zamrożenia aplikacji jest rozszerzeniem tego obowiązku monitorowania.
Śledzenie stanów aplikacji z usług systemowych
Usługi systemowe działające w system_server lub jako natywne demony mogą też używać opisanych wcześniej interfejsów API do śledzenia ważności i stanu zamrożenia procesów aplikacji:
ActivityManager.addOnUidImportanceListener: Usługi systemowe mogą rejestrować odbiorcę, aby śledzić zmiany ważności identyfikatora UID. Podczas odbierania połączenia lub oddzwaniania z aplikacji usługa może używaćBinder.getCallingUid(), aby uzyskać identyfikator UID i powiązać go ze stanem ważności śledzonym przez odbiornik. Dzięki temu usługi systemowe wiedzą, czy aplikacja wywołująca jest w stanie buforowanym.IBinder.addFrozenStateChangeCallback: gdy usługa systemowa otrzyma obiekt Binder z aplikacji (np. w ramach rejestracji wywołań zwrotnych), powinna zarejestrowaćFrozenStateChangeCallbackw konkretnej instancjiIBinder. Bezpośrednio powiadamia system o zamrożeniu lub odmrożeniu procesu aplikacji hostującego ten binder.
Rekomendacje dotyczące usług systemowych
Zalecamy, aby wszystkie usługi systemowe, które mogą wchodzić w interakcje z aplikacjami, śledziły stan pamięci podręcznej i zamrożenia procesów aplikacji, z którymi się komunikują. Jeśli tego nie zrobisz, może to spowodować:
- Zużycie zasobów: wykonywanie pracy na potrzeby aplikacji, które są w pamięci podręcznej i nie są widoczne dla użytkownika, może powodować marnowanie zasobów systemowych.
- Awarie aplikacji: synchroniczne wywołania interfejsu do zamrożonych aplikacji powodują ich awarię. Asynchroniczne wywołania interfejsu Binder do zamrożonych aplikacji powodują awarię, jeśli przepełni się ich asynchroniczny bufor transakcji.
- Nieoczekiwane działanie aplikacji: aplikacje, które nie są już zamrożone, od razu otrzymają wszystkie buforowane asynchroniczne transakcje interfejsu Binder, które zostały do nich wysłane, gdy były zamrożone. Aplikacje mogą spędzić w zamrażarce nieokreślony czas, więc transakcje w buforze mogą być bardzo nieaktualne.
Usługi systemowe często używają RemoteCallbackList do zarządzania wywołaniami zwrotnymi na urządzeniu zdalnym i automatycznego obsługiwania martwych procesów. Aby obsługiwać zamrożone aplikacje, rozszerz istniejące użycie RemoteCallbackList, stosując zasadę zamrożonego wywołującego zgodnie z opisem w sekcji Używanie RemoteCallbackList.