Procesy w tle mogą wymagać dużo pamięci i baterii. Na przykład transmisja pośrednia może uruchamiać wiele procesów w tle, które zarejestrowały jej nasłuchiwanie, nawet jeśli nie są one zbyt intensywne. Może to mieć znaczny wpływ na wydajność urządzenia i wrażenia użytkowników.
Aby rozwiązać ten problem, Android 7.0 (poziom interfejsu API 24) stosuje te ograniczenia:
- Aplikacje kierowane na Androida 7.0 (poziom interfejsu API 24) lub nowszego nie otrzymują komunikatów
CONNECTIVITY_ACTION
, jeśli w pliku manifestu zadeklarują swojego odbiornika. Aplikacje nadal będą otrzymywać komunikatyCONNECTIVITY_ACTION
, jeśli zarejestrujeszBroadcastReceiver
wContext.registerReceiver()
, a ten kontekst będzie nadal prawidłowy. - Aplikacje nie mogą wysyłać ani odbierać komunikatów
ACTION_NEW_PICTURE
aniACTION_NEW_VIDEO
. Ta optymalizacja wpływa na wszystkie aplikacje, nie tylko na te kierowane na Androida 7.0 (poziom API 24).
Jeśli Twoja aplikacja korzysta z tych intencji, jak najszybciej usuń zależności, by umożliwić prawidłowe kierowanie reklam na urządzenia z Androidem 7.0 lub nowszym. Platforma Android udostępnia kilka rozwiązań, które zmniejszą zapotrzebowanie na takie komunikaty pośrednie. 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 pomiaru. Teraz możesz też używać polecenia JobScheduler
, aby reagować na zmiany dotyczące dostawców treści. Obiekty JobInfo
zawierają parametry, których JobScheduler
używa do planowania zadania. Gdy warunki zadania są spełnione, system wykonuje je w JobService
Twojej aplikacji.
Na tej stronie dowiesz się, jak używać alternatywnych metod, takich jak JobScheduler
, aby dostosować aplikację do nowych ograniczeń.
Ograniczenia inicjowane przez użytkownika
Na stronie Wykorzystanie baterii w ustawieniach systemu użytkownik może wybrać jedną z tych opcji:
- Bez ograniczeń: zezwalaj na pracę w tle, co może zwiększyć wykorzystanie baterii.
- Zoptymalizowana (domyślnie): optymalizuje możliwość działania aplikacji w tle na podstawie interakcji użytkownika z aplikacją.
- Z ograniczeniami: całkowicie uniemożliwia aplikacji działanie w tle. Aplikacje mogą nie działać zgodnie z oczekiwaniami.
Jeśli aplikacja wykazuje niektóre z niewłaściwych działań opisanych w artykule na temat Androida, system może poprosić użytkownika o ograniczenie dostępu do zasobów systemowych.
Jeśli system wykryje, że aplikacja zużywa nadmiarowe zasoby, powiadomi o tym użytkownika i umożliwi ograniczenie działań w aplikacji. Działania, które mogą wywołać takie powiadomienie:
- Nadmierna blokada uśpienia: 1 częściowa blokada uśpienia przechowywana na godzinę przy wyłączonym ekranie
- Nadmierne usługi w tle: jeśli aplikacja jest kierowana na interfejsy API niższe niż 26 i ma zbyt wiele usług w tle
Dokładne ograniczenia są określane przez producenta urządzenia. Na przykład w kompilacjach AOSP z Androidem 9 (poziom interfejsu API 28) lub nowszym aplikacje działające w tle, które są objęte ograniczeniami, podlegają tym ograniczeniom:
- Nie udało się uruchomić usług działających na pierwszym planie
- Istniejące usługi na pierwszym planie zostaną usunięte
- Alarmy nie są uruchamiane
- Zadania nie są wykonywane
Jeśli aplikacja jest kierowana na Androida 13 (poziom interfejsu API 33) lub nowszego i ma stan „zastrzeżony”, system nie wykona transmisji BOOT_COMPLETED
ani LOCKED_BOOT_COMPLETED
, dopóki aplikacja nie zostanie uruchomiona z innych powodów.
Konkretne ograniczenia znajdziesz w sekcji Ograniczenia zarządzania zasilaniem.
Ograniczenia w odbieraniu komunikatów o aktywności w sieci
Aplikacje kierowane na Androida 7.0 (poziom interfejsu API 24) nie otrzymują komunikatów CONNECTIVITY_ACTION
, jeśli zarejestrują się, aby je otrzymywać w pliku manifestu, a procesy zależne od tej transmisji nie zostaną uruchomione. Może to powodować problemy w przypadku aplikacji, które chcą nasłuchiwać zmian w sieci lub wykonywać zbiorcze działania sieciowe, gdy urządzenie łączy się z siecią bez pomiaru. Istnieje już kilka rozwiązań, które pozwalają ominąć to ograniczenie, ale wybór odpowiedniego zależy od tego, co chcesz osiągnąć przez aplikację.
Uwaga: użytkownik BroadcastReceiver
zarejestrowany w usłudze Context.registerReceiver()
otrzymuje te komunikaty, gdy aplikacja jest uruchomiona.
Zaplanuj zadania sieciowe dla połączeń bez pomiaru
Jeśli do tworzenia obiektu JobInfo
używasz klasy JobInfo.Builder
, zastosuj metodę setRequiredNetworkType()
i przekaż JobInfo.NETWORK_TYPE_UNMETERED
jako parametr zadania. Ten przykładowy kod umożliwia zaplanowanie uruchomienia usługi, gdy urządzenie łączy się z siecią bez pomiaru użycia danych i jest ładowane:
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 zostaną spełnione warunki Twojego zadania, aplikacja otrzyma wywołanie zwrotne, aby uruchomić metodę onStartJob()
we wskazanym obiekcie JobService.class
. Więcej przykładów implementacji JobScheduler
znajdziesz w przykładowej aplikacji JobScheduler.
Alternatywą dla JobScheduler jest interfejs API WorkManager, który umożliwia planowanie zadań w tle, które wymagają gwarantowanego ukończenia, niezależnie od tego, czy proces aplikacji trwa. WorkManager wybiera odpowiedni sposób uruchomienia zadania (bezpośrednio w wątku w procesie aplikacji albo za pomocą JobScheduler, FirebaseJobDispatcher lub AlarmManager) na podstawie takich czynników jak poziom interfejsu API urządzenia. Poza tym WorkManager nie wymaga Usług Google Play i oferuje kilka zaawansowanych funkcji, takich jak łączenie zadań w łańcuch czy sprawdzanie stanu zadań. Więcej informacji znajdziesz w artykule WorkManager.
Monitoruj połączenia sieciowe, gdy aplikacja jest uruchomiona
Aktywne aplikacje mogą nadal nasłuchiwać na CONNECTIVITY_CHANGE
przy zarejestrowanym elemencie BroadcastReceiver
. Interfejs ConnectivityManager
API zapewnia jednak bardziej niezawodną metodę żądania wywołania zwrotnego tylko w przypadku spełnienia określonych warunków sieciowych.
Obiekty NetworkRequest
definiują parametry wywołania zwrotnego sieci w zakresie NetworkCapabilities
. Obiekty NetworkRequest
tworzysz za pomocą klasy NetworkRequest.Builder
. registerNetworkCallback()
przekazuje następnie obiekt NetworkRequest
do systemu. Gdy zostaną spełnione warunki sieci, aplikacja otrzyma wywołanie zwrotne, by wykonać metodę onAvailable()
zdefiniowaną w jej klasie ConnectivityManager.NetworkCallback
.
Aplikacja będzie otrzymywać wywołania zwrotne, dopóki jej nie zamknie lub nie wywoła wywołania unregisterNetworkCallback()
.
Ograniczenia w zakresie odbierania transmisji obrazów i filmów
Na Androidzie 7.0 (poziom interfejsu API 24) aplikacje nie mogą wysyłać ani odbierać komunikatów ACTION_NEW_PICTURE
i ACTION_NEW_VIDEO
. To ograniczenie pomaga złagodzić wydajność i wrażenia użytkowników, gdy kilka aplikacji musi się wybudzić, aby przetworzyć nowy obraz lub film. Android 7.0 (poziom interfejsu API 24) stanowi rozszerzenie JobInfo
i JobParameters
, aby zapewnić alternatywne rozwiązanie.
Aktywuj zadania w przypadku zmian identyfikatora URI treści
Aby aktywować zadania w przypadku zmian identyfikatorów URI treści, Android 7.0 (poziom interfejsu API 24) rozszerza interfejs API JobInfo
o te metody:
-
JobInfo.TriggerContentUri()
- Zawiera parametry wymagane do aktywowania zadania w przypadku zmian identyfikatora URI treści.
-
JobInfo.Builder.addTriggerContentUri()
- Przekazuje obiekt
TriggerContentUri
doJobInfo
.ContentObserver
monitoruje identyfikator URI treści w zamkniętej treści. Jeśli z zadaniem jest powiązanych wiele obiektówTriggerContentUri
, system udostępnia wywołanie zwrotne, nawet jeśli zgłasza zmianę tylko w jednym z identyfikatorów URI treści. - Dodaj flagę
TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS
, aby aktywować zadanie w przypadku zmiany któregokolwiek elementu podrzędnego danego identyfikatora URI. Ta flaga odpowiada parametrowinotifyForDescendants
przekazanemu doregisterContentObserver()
.
Uwaga: TriggerContentUri()
nie można używać w połączeniu z setPeriodic()
ani setPersisted()
. Aby stale monitorować zmiany treści, zaplanuj nowy zasób JobInfo
, zanim JobService
aplikacji zakończy obsługę ostatniego wywołania zwrotnego.
Ten przykładowy kod umożliwia zaplanowanie aktywowania zadania po zgłoszeniu przez system zmiany 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 obiekt JobParameters
zostanie przekazany do metody onStartJob()
w obiekcie MediaContentJob.class
.
Ustalenie, które organy administracji treści 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 urzędy treści i identyfikatory URI wywołały zadanie:
-
Uri[] getTriggeredContentUris()
- Zwraca tablicę identyfikatorów URI, które aktywowały zadanie. Będzie mieć wartość
null
, jeśli zadanie nie zostało aktywowane przez żadne identyfikatory URI (np. zadanie zostało aktywowane z powodu określonego terminu lub z innego powodu) lub liczba zmienionych identyfikatorów URI jest większa niż 50. -
String[] getTriggeredContentAuthorities()
- Zwraca tablicę ciągów znaków z urzędami treści, które aktywowały zadanie.
Jeśli zwrócona tablica nie jest wartością
null
, użyj funkcjigetTriggeredContentUris()
, aby pobrać szczegóły dotyczące zmienionych identyfikatorów URI.
Ten przykładowy kod zastępuje metodę JobService.onStartJob()
i rejestruje urzędy treści oraz identyfikatory URI, które spowodowały uruchomienie zadania:
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
Optymalizacja aplikacji pod kątem działania na urządzeniach z małą ilością pamięci lub przy 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 zarejestrowanych w pliku manifestu może pomóc w lepszym działaniu aplikacji na takich urządzeniach. Chociaż Android 7.0 (poziom interfejsu API 24) ogranicza niektóre z tych problemów, zalecamy zoptymalizowanie aplikacji pod kątem działania bez korzystania z tych procesów w tle.
Podane niżej polecenia Android Debug Bridge (ADB) pomogą Ci przetestować działanie aplikacji przy wyłączonych procesach w tle:
- Aby symulować warunki, w których niejawne komunikaty i usługi w tle są niedostępne, wpisz to polecenie:
-
$ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore
- Aby ponownie włączyć niejawne komunikaty i usługi w tle, wpisz to polecenie:
-
$ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow
- Możesz zasymulować użytkownika, który wprowadza w aplikacji stan „ograniczony” w celu zużycia baterii w tle. To ustawienie uniemożliwia uruchamianie aplikacji w tle. Aby to zrobić, uruchom następujące polecenie w oknie terminala:
-
$ adb shell cmd appops set <PACKAGE_NAME> RUN_ANY_IN_BACKGROUND deny