Procesy w tle mogą zużywać dużo pamięci i baterii. Na przykład niejawna transmisja może uruchomić wiele procesów w tle, które zarejestrowały się, aby jej nasłuchiwać, nawet jeśli te procesy nie wykonują zbyt wielu zadań. Może to mieć duży wpływ na wydajność urządzenia i wygodę użytkownika.
Aby rozwiązać ten problem, Android 7.0 (poziom interfejsu API 24) wprowadza te ograniczenia:
- Aplikacje kierowane na Androida 7.0 (poziom interfejsu API 24) i nowsze nie otrzymują
CONNECTIVITY_ACTION
transmisji, jeśli zadeklarują odbiornik transmisji w manifeście. Aplikacje nadal będą otrzymywać transmisjeCONNECTIVITY_ACTION
, jeśli zarejestrują swojeBroadcastReceiver
wContext.registerReceiver()
, a kontekst będzie nadal ważny. - Aplikacje nie mogą wysyłać ani odbierać transmisji
ACTION_NEW_PICTURE
aniACTION_NEW_VIDEO
. Ta optymalizacja dotyczy wszystkich aplikacji, nie tylko tych kierowanych na Androida 7.0 (poziom API 24).
Jeśli Twoja aplikacja korzysta z któregoś z tych intencji, jak najszybciej usuń od nich zależności, aby móc prawidłowo kierować reklamy na urządzenia z Androidem 7.0 lub nowszym. Platforma Androida udostępnia kilka rozwiązań, które ograniczają potrzebę korzystania z tych niejawnych transmisji. Na przykład JobScheduler
i nowy WorkManager zapewniają niezawodne mechanizmy planowania operacji sieciowych po spełnieniu określonych warunków, takich jak połączenie z siecią bez limitu danych. Możesz teraz też używać JobScheduler
do reagowania na zmiany u dostawców treści. Obiekty JobInfo
zawierają parametry, których JobScheduler
używa do planowania zadania. Gdy warunki zadania zostaną spełnione, system wykona je w JobService
aplikacji.
Na tej stronie dowiesz się, jak za pomocą alternatywnych metod, np. JobScheduler
, dostosować aplikację do tych nowych ograniczeń.
Ograniczenia zainicjowane przez użytkownika
Na stronie Zużycie baterii w ustawieniach systemu użytkownik może wybrać jedną z tych opcji:
- Nieograniczone:zezwalaj na wszystkie działania w tle, co może powodować większe zużycie baterii.
- Zoptymalizowane (domyślne): optymalizuje możliwość wykonywania przez aplikację pracy w tle na podstawie sposobu, w jaki użytkownik z niej korzysta.
- Ograniczone: całkowicie uniemożliwia działanie aplikacji w tle. Aplikacje mogą nie działać zgodnie z oczekiwaniami.
Jeśli aplikacja wykazuje niektóre z niepożądanych zachowań opisanych w Android Vitals, system może poprosić użytkownika o ograniczenie dostępu tej aplikacji do zasobów systemowych.
Jeśli system zauważy, że aplikacja zużywa zbyt dużo zasobów, powiadomi o tym użytkownika i da mu możliwość ograniczenia działań aplikacji. Zachowania, które mogą spowodować wyświetlenie powiadomienia, to:
- Zbyt wiele blokad uśpienia: 1 częściowa blokada uśpienia utrzymywana przez godzinę, gdy ekran jest wyłączony
- Nadmierna liczba usług działających w tle: jeśli aplikacja jest kierowana na poziomy interfejsu API niższe niż 26 i ma nadmierną liczbę usług działających w tle
Dokładne ograniczenia są określane przez producenta urządzenia. Na przykład w przypadku kompilacji AOSP z Androidem 9 (API na poziomie 28) lub nowszym aplikacje działające w tle, które są w stanie „ograniczonym”, mają następujące ograniczenia:
- Nie można uruchomić usług działających na pierwszym planie
- Istniejące usługi działające na pierwszym planie są usuwane z pierwszego planu.
- Alarmy nie są włączane
- Zadania nie są wykonywane
Jeśli aplikacja jest kierowana na Androida 13 (API na poziomie 33) lub nowszego i jest w stanie „ograniczonym”, system nie dostarcza transmisji BOOT_COMPLETED
ani transmisji LOCKED_BOOT_COMPLETED
, dopóki aplikacja nie zostanie uruchomiona z innych powodów.
Konkretne ograniczenia są wymienione w sekcji Ograniczenia dotyczące zarządzania energią.
Ograniczenia dotyczące odbierania transmisji aktywności sieciowej
Aplikacje kierowane na Androida 7.0 (poziom interfejsu API 24) nie otrzymują transmisji CONNECTIVITY_ACTION
, jeśli zarejestrują się w celu ich otrzymywania w pliku manifestu, a procesy, które zależą od tej transmisji, nie zostaną uruchomione. Może to stanowić problem dla aplikacji, które chcą nasłuchiwać zmian w sieci lub wykonywać zbiorcze działania sieciowe, gdy urządzenie połączy się z siecią bez limitu danych. W platformie Android istnieje już kilka rozwiązań, które pozwalają obejść to ograniczenie, ale wybór odpowiedniego zależy od tego, co chcesz osiągnąć w swojej aplikacji.
Uwaga: BroadcastReceiver
zarejestrowany w
Context.registerReceiver()
nadal otrzymuje te transmisje, gdy aplikacja jest uruchomiona.
Planowanie zadań sieciowych w przypadku połączeń bez limitu danych
Podczas tworzenia obiektu JobInfo
za pomocą klasy JobInfo.Builder
zastosuj metodę setRequiredNetworkType()
i przekaż JobInfo.NETWORK_TYPE_UNMETERED
jako parametr zadania. Poniższy przykładowy kod planuje uruchomienie usługi, gdy urządzenie połączy się z siecią bez limitu danych i będzie się ładować:
Kotlin
const val MY_BACKGROUND_JOB = 0 ... fun scheduleJob(context: Context) { val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler val job = JobInfo.Builder( MY_BACKGROUND_JOB, ComponentName(context, MyJobService::class.java) ) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) .setRequiresCharging(true) .build() jobScheduler.schedule(job) }
Java
public static final int MY_BACKGROUND_JOB = 0; ... public static void scheduleJob(Context context) { JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo job = new JobInfo.Builder( MY_BACKGROUND_JOB, new ComponentName(context, MyJobService.class)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) .setRequiresCharging(true) .build(); js.schedule(job); }
Gdy warunki zadania zostaną spełnione, aplikacja otrzyma wywołanie zwrotne, aby uruchomić metodę onStartJob()
w określonym JobService.class
. Więcej przykładów implementacji JobScheduler
znajdziesz w przykładowej aplikacji JobScheduler.
Nową alternatywą dla JobScheduler jest WorkManager, interfejs API, który umożliwia planowanie zadań w tle, które muszą zostać wykonane niezależnie od tego, czy proces aplikacji jest aktywny. WorkManager wybiera odpowiedni sposób uruchomienia zadania (bezpośrednio w wątku w procesie aplikacji, a także za pomocą JobScheduler, FirebaseJobDispatcher lub AlarmManager) na podstawie takich czynników jak poziom interfejsu API urządzenia. WorkManager nie wymaga też usług Play i zapewnia kilka zaawansowanych funkcji, takich jak łączenie zadań w łańcuchy czy sprawdzanie stanu zadania. Więcej informacji znajdziesz w artykule WorkManager.
Monitorowanie połączenia sieciowego podczas działania aplikacji
Aplikacje, które są uruchomione, nadal mogą nasłuchiwać CONNECTIVITY_CHANGE
za pomocą zarejestrowanego BroadcastReceiver
. Interfejs ConnectivityManager
API zapewnia jednak bardziej niezawodną metodę żądania wywołania zwrotnego tylko wtedy, gdy spełnione są określone warunki sieciowe.
Obiekty NetworkRequest
określają parametry wywołania zwrotnego sieci w terminach NetworkCapabilities
. Obiekty NetworkRequest
tworzysz za pomocą klasy NetworkRequest.Builder
. registerNetworkCallback()
przekazuje następnie obiekt NetworkRequest
do systemu. Gdy warunki sieciowe zostaną spełnione, aplikacja otrzyma wywołanie zwrotne, aby wykonać metodę onAvailable()
zdefiniowaną w klasie ConnectivityManager.NetworkCallback
.
Aplikacja nadal będzie otrzymywać wywołania zwrotne, dopóki nie zostanie zamknięta lub nie wywoła funkcji unregisterNetworkCallback()
.
Ograniczenia dotyczące odbierania transmisji obrazów i filmów
W Androidzie 7.0 (interfejs API na poziomie 24) aplikacje nie mogą wysyłać ani odbierać transmisji ACTION_NEW_PICTURE
ani ACTION_NEW_VIDEO
. To ograniczenie pomaga zmniejszyć wpływ na wydajność i komfort użytkownika, gdy kilka aplikacji musi się uaktywnić, aby przetworzyć nowy obraz lub film. Android 7.0 (poziom interfejsu API 24) rozszerza JobInfo
i JobParameters
, aby zapewnić alternatywne rozwiązanie.
Aktywowanie zadań w przypadku zmian identyfikatora URI treści
Aby wywoływać zadania przy zmianach identyfikatora URI treści, Android 7.0 (poziom interfejsu API 24) rozszerza interfejs JobInfo
o te metody:
-
JobInfo.TriggerContentUri()
- Zawiera parametry wymagane do wywołania zadania w przypadku zmian identyfikatora URI treści.
-
JobInfo.Builder.addTriggerContentUri()
-
Przekazuje obiekt
TriggerContentUri
doJobInfo
.ContentObserver
monitoruje identyfikator URI zamkniętych treści. Jeśli z danym zadaniem jest powiązanych kilka obiektówTriggerContentUri
, system wywołuje funkcję zwrotną nawet wtedy, gdy zgłasza zmianę tylko w jednym z identyfikatorów URI treści. -
Dodaj flagę
TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS
, aby uruchomić zadanie, jeśli zmieni się którykolwiek z potomków danego identyfikatora URI. Ten flag odpowiada parametrowinotifyForDescendants
przekazywanemu doregisterContentObserver()
.
Uwaga: parametru TriggerContentUri()
nie można używać w połączeniu z parametrami setPeriodic()
ani setPersisted()
. Aby stale monitorować zmiany treści, zaplanuj nowe JobInfo
, zanim JobService
aplikacji zakończy obsługę ostatniego wywołania zwrotnego.
Poniższy przykładowy kod planuje zadanie, które ma być wywoływane, gdy system zgłosi zmianę identyfikatora URI treści, MEDIA_URI
:
Kotlin
const val MY_BACKGROUND_JOB = 0 ... fun scheduleJob(context: Context) { val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler val job = JobInfo.Builder( MY_BACKGROUND_JOB, ComponentName(context, MediaContentJob::class.java) ) .addTriggerContentUri( JobInfo.TriggerContentUri( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS ) ) .build() jobScheduler.schedule(job) }
Java
public static final int MY_BACKGROUND_JOB = 0; ... public static void scheduleJob(Context context) { JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo.Builder builder = new JobInfo.Builder( MY_BACKGROUND_JOB, new ComponentName(context, MediaContentJob.class)); builder.addTriggerContentUri( new JobInfo.TriggerContentUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS)); js.schedule(builder.build()); }
Gdy system zgłosi zmianę w określonych identyfikatorach URI treści, aplikacja otrzyma wywołanie zwrotne, a do metody onStartJob()
w MediaContentJob.class
zostanie przekazany obiekt JobParameters
.
Określanie, które instytucje zarządzające treściami wywołały zadanie
Android 7.0 (poziom interfejsu API 24) rozszerza też JobParameters
, aby umożliwić aplikacji otrzymywanie przydatnych informacji o tym, które organy treści i adresy URI wywołały zadanie:
-
Uri[] getTriggeredContentUris()
-
Zwraca tablicę adresów URI, które wywołały zadanie. Wartość ta będzie wynosić
null
, jeśli żadne identyfikatory URI nie spowodowały uruchomienia zadania (np. zadanie zostało uruchomione z powodu terminu lub z innego powodu) albo liczba zmienionych identyfikatorów URI jest większa niż 50. -
String[] getTriggeredContentAuthorities()
-
Zwraca tablicę ciągów zawierającą podmioty uprawnione do treści, które wywołały zadanie.
Jeśli zwrócona tablica nie jest pusta (
null
), użyjgetTriggeredContentUris()
, aby pobrać szczegóły dotyczące tego, które adresy URI uległy zmianie.
Poniższy przykładowy kod zastępuje metodę JobService.onStartJob()
i rejestruje organy treści oraz identyfikatory URI, które wywołały zadanie:
Kotlin
override fun onStartJob(params: JobParameters): Boolean { StringBuilder().apply { append("Media content has changed:\n") params.triggeredContentAuthorities?.also { authorities -> append("Authorities: ${authorities.joinToString(", ")}\n") append(params.triggeredContentUris?.joinToString("\n")) } ?: append("(No content)") Log.i(TAG, toString()) } return true }
Java
@Override public boolean onStartJob(JobParameters params) { StringBuilder sb = new StringBuilder(); sb.append("Media content has changed:\n"); if (params.getTriggeredContentAuthorities() != null) { sb.append("Authorities: "); boolean first = true; for (String auth : params.getTriggeredContentAuthorities()) { if (first) { first = false; } else { sb.append(", "); } sb.append(auth); } if (params.getTriggeredContentUris() != null) { for (Uri uri : params.getTriggeredContentUris()) { sb.append("\n"); sb.append(uri); } } } else { sb.append("(No content)"); } Log.i(TAG, sb.toString()); return true; }
Dalsza optymalizacja aplikacji
Zoptymalizowanie aplikacji pod kątem działania na urządzeniach z małą ilością pamięci lub w warunkach małej ilości pamięci może poprawić wydajność i wygodę użytkowników. Usunięcie zależności od usług działających w tle i niejawnych odbiorników transmisji zarejestrowanych w manifeście może poprawić działanie aplikacji na takich urządzeniach. Android 7.0 (poziom interfejsu API 24) podejmuje działania mające na celu ograniczenie niektórych z tych problemów, ale zalecamy zoptymalizowanie aplikacji tak, aby działała bez użycia tych procesów w tle.
Te polecenia Android Debug Bridge (ADB) pomogą Ci przetestować działanie aplikacji przy wyłączonych procesach w tle:
- Aby zasymulować warunki, w których nie są dostępne niejawne transmisje i usługi działające w tle, wpisz to polecenie:
-
$ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore
- Aby ponownie włączyć niejawne transmisje i usługi działające w tle, wpisz to polecenie:
-
$ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow
- Możesz symulować umieszczenie aplikacji w stanie „ograniczonym” w przypadku zużycia baterii w tle. To ustawienie uniemożliwia działanie aplikacji w tle. Aby to zrobić, uruchom w oknie terminala to polecenie:
-
$ adb shell cmd appops set <PACKAGE_NAME> RUN_ANY_IN_BACKGROUND deny