Optymalizacja w tle

Procesy w tle mogą zużywać dużo pamięci i energii. Na przykład transmisja niejawna może uruchomić wiele procesów w tle, które zarejestrowały się jako odbiorniki, nawet jeśli nie wykonują one zbyt wielu operacji. Może to mieć znaczny wpływ na wydajność urządzenia i wygodę użytkownika.

Aby rozwiązać ten problem, w Androidzie 7.0 (poziom interfejsu API 24) obowiązują te ograniczenia:

  • Aplikacje kierowane na Androida 7.0 (poziom interfejsu API 24) lub nowszego nie otrzymują transmisji CONNECTIVITY_ACTION, jeśli w manifeście deklarują odbiornik transmisji. Aplikacje nadal będą otrzymywać transmisje CONNECTIVITY_ACTION, jeśli ich BroadcastReceiver zostanie zarejestrowany w Context.registerReceiver(), a ten kontekst nadal będzie ważny.
  • Aplikacje nie mogą wysyłać ani odbierać transmisji ACTION_NEW_PICTURE ani ACTION_NEW_VIDEO. Ta optymalizacja dotyczy wszystkich aplikacji, a nie tylko tych kierowanych na Androida 7.0 (poziom API 24).

Jeśli Twoja aplikacja używa któregoś z tych intencji, jak najszybciej usuń te zależności, aby móc odpowiednio kierować reklamy na urządzenia z Androidem 7.0 lub nowszym. Platforma Android oferuje kilka rozwiązań, które ograniczają potrzebę korzystania z tych niejawnych transmisji. Na przykład JobScheduler i nowy WorkManager zapewniają niezawodne mechanizmy do planowania operacji sieciowych, gdy spełnione są określone warunki, takie jak połączenie z siecią bez limitu. Możesz też teraz 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 to zadanie w Twojej aplikacji za pomocą JobService.

Na tej stronie dowiesz się, jak za pomocą metod alternatywnych, takich jak JobScheduler, dostosować aplikację do nowych ograniczeń.

Ograniczenia inicjowane przez użytkownika

Na stronie Użycie baterii w ustawieniach systemu użytkownik może wybrać jedną z tych opcji:

  • Nieograniczone: zezwalaj na wszystkie działania w tle, które mogą zużywać więcej baterii.
  • Zoptymalizowana (domyślnie): optymalizuje działanie aplikacji w tle na podstawie interakcji użytkownika z aplikacją.
  • Ograniczone:całkowicie uniemożliwia aplikacji działanie 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 aplikacji do zasobów systemowych.

Jeśli system zauważy, że aplikacja zużywa nadmierne zasoby, powiadomi użytkownika i zaoferuje mu możliwość ograniczenia działań aplikacji. Zachowania, które mogą spowodować wyświetlenie powiadomienia:

  • Zbyt wiele blokad uśpienia: 1 częściowa blokada uśpienia utrzymująca się przez godzinę, gdy ekran jest wyłączony
  • Nadmierne usługi działające w tle: jeśli aplikacja jest kierowana na poziomy interfejsu API niższe niż 26 i ma nadmierne usługi działające w tle.

Dokładne ograniczenia są określane przez producenta urządzenia. Na przykład w kompilacji AOSP z Androidem 9 (poziom interfejsu API 28) lub nowszym aplikacje działające w tle w stanie „ograniczony” mają te ograniczenia:

  • Nie można uruchomić usług na pierwszym planie
  • Istniejące usługi na pierwszym planie są usuwane z pierwszego planu.
  • Alarmy nie są aktywowane
  • Zadania nie są wykonywane

Jeśli aplikacja jest kierowana na Androida 13 (API na poziomie 33) lub nowszego i jest w stanie „ograniczony”, system nie dostarcza transmisji BOOT_COMPLETED ani LOCKED_BOOT_COMPLETED, dopóki aplikacja nie zostanie uruchomiona z innych powodów.

Szczegółowe ograniczenia są wymienione w  ograniczeniach zarządzania zasilaniem.

Ograniczenia dotyczące odbierania transmisji aktywności sieci

Aplikacje kierowane na Androida 7.0 (poziom interfejsu API 24) nie otrzymują transmisji CONNECTIVITY_ACTION, jeśli rejestrują się w tym celu w manifeście, a procesy zależne od tej transmisji nie będą się uruchamiać. Może to stanowić problem dla aplikacji, które chcą nasłuchiwać zmian w sieci lub wykonywać zbiorcze działania sieciowe, gdy urządzenie łączy się z siecią bez limitu. W ramach platformy Androida istnieje już kilka rozwiązań umożliwiających obejście tego ograniczenia, ale wybór odpowiedniego rozwiązania zależy od tego, czego oczekujesz od aplikacji.

Uwaga: BroadcastReceiver zarejestrowany w Context.registerReceiver() nadal odbiera te transmisje, gdy aplikacja jest uruchomiona.

Planowanie zadań sieciowych na połączeniach bezpłatnych

Jeśli używasz klasy JobInfo.Builder do tworzenia obiektu JobInfo, zastosuj metodę setRequiredNetworkType() i przekaż parametr JobInfo.NETWORK_TYPE_UNMETERED jako parametr zadania. Poniższy przykładowy kod planuje uruchomienie usługi, gdy urządzenie połączy się z nielimitowaną siecią i będzie ł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 warunki zadania zostaną spełnione, aplikacja otrzyma wywołanie zwrotne, aby wykonać metodę onStartJob() w określonym obiekcie 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 wymagają gwarantowanego ukończenia, niezależnie od tego, czy proces aplikacji jest dostępny. WorkManager wybiera odpowiedni sposób wykonania zadania (bezpośrednio w wątku w procesie aplikacji lub za pomocą JobSchedulera, FirebaseJobDispatchera lub AlarmManagera) na podstawie takich czynników jak poziom interfejsu API urządzenia. WorkManager nie wymaga Usług Google Play i zawiera kilka zaawansowanych funkcji, takich jak łączenie zadań lub sprawdzanie ich stanu. Więcej informacji znajdziesz w artykule WorkManager.

Monitorowanie połączenia z internetem podczas działania aplikacji

Uruchomione aplikacje mogą nadal nasłuchiwać CONNECTIVITY_CHANGE z zarejestrowanym BroadcastReceiver. Interfejs API ConnectivityManager zapewnia jednak bardziej niezawodną metodę żądania połączenia zwrotnego tylko wtedy, gdy są spełnione określone warunki sieciowe.

Obiekty NetworkRequest definiują parametry wywołania zwrotnego sieci w terminach NetworkCapabilities. Tworzysz obiekty NetworkRequest za pomocą klasy NetworkRequest.Builder. registerNetworkCallback()przekazuje 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 będzie otrzymywać wywołania zwrotne, dopóki nie zostanie zamknięta lub nie wywoła funkcji unregisterNetworkCallback().

Ograniczenia dotyczące odbierania transmisji obrazu i dźwięku

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 ograniczyć wpływ na wydajność i wrażenia użytkownika, gdy kilka aplikacji musi się aktywować, aby przetworzyć nowy obraz lub film. Android 7.0 (poziom interfejsu API 24) rozszerza JobInfoJobParameters, aby zapewnić alternatywne rozwiązanie.

Uruchamianie zadań po zmianach identyfikatora URI treści

Aby uruchamiać zadania po zmianach identyfikatora URI treści, Android 7.0 (poziom interfejsu API 24) rozszerza interfejs API JobInfo o te metody:

JobInfo.TriggerContentUri()
Obejmuje parametry wymagane do wywołania zadania po zmianie identyfikatora URI treści.
JobInfo.Builder.addTriggerContentUri()
Przesyła obiekt TriggerContentUri do obiektu JobInfo. ContentObservermonitoruje zakapsułowany identyfikator URI treści. Jeśli z zadaniem powiązanych jest wiele obiektów TriggerContentUri, system wywołuje funkcję zwracającą dane, nawet jeśli zgłoszona zmiana dotyczy tylko jednego z identyfikatorów URI treści.
Dodaj oznaczenie TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS, aby uruchomić zadanie, jeśli zmieni się którykolwiek z potomków danego identyfikatora URI. Ten parametr odpowiada parametrowi notifyForDescendants przekazanemu do funkcji registerContentObserver().

Uwaga: nie można użyć funkcji TriggerContentUri() w połączeniu z funkcjami setPeriodic() lub setPersisted(). Aby stale sprawdzać, czy nie nastąpiły zmiany treści, zaplanuj nowe JobInfo, zanim funkcja JobService w aplikacji zakończy obsługę ostatniego wywołania zwrotnego.

Poniższy przykładowy kod powoduje uruchomienie zadania, 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 wybranych identyfikatorach URI treści, Twoja aplikacja otrzyma wywołanie zwrotne, a obiekt JobParameters zostanie przekazany metodzie onStartJob()MediaContentJob.class.

Określanie, które instytucje zajmujące się treściami wywołały zadanie

Android 7.0 (poziom interfejsu API 24) rozszerza uprawnienia JobParameters, aby umożliwić aplikacji otrzymywanie przydatnych informacji o tym, jakie autorytety treści i adresy URI wywołały zadanie:

Uri[] getTriggeredContentUris()
Zwraca tablicę identyfikatorów URI, które wywołały zadanie. Będzie to null, jeśli żadne adresy URI nie wywołały zadania (na przykład zostało ono wywołane z powodu terminu lub z innego powodu) albo liczba zmienionych adresów URI przekracza 50.
String[] getTriggeredContentAuthorities()
Zwraca tablicę ciągów znaków z autorytetami treści, które wywołały zadanie. Jeśli zwrócony tablica nie jest null, użyj getTriggeredContentUris(), aby pobrać szczegóły dotyczące tego, które adresy URI się zmieniły.

Poniższy przykładowy kod zastępuje metodę JobService.onStartJob() i rejestruje autorytety 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 w tle i niejawnych odbiorników transmisji zarejestrowanych w manifeście może poprawić działanie aplikacji na takich urządzeniach. Chociaż Android 7.0 (poziom interfejsu API 24) zawiera rozwiązania niektórych z tych problemów, zalecamy zoptymalizowanie aplikacji tak, aby całkowicie zrezygnować z tych procesów w tle.

Aby przetestować działanie aplikacji z wyłączonymi procesami w tle, możesz użyć tych poleceń Android Debug Bridge (ADB):

  • Aby symulować warunki, w których transmisje niejawne 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ć transmisje i usługi w tle, wpisz to polecenie:
  • $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow
    
  • Możesz symulować sytuację, w której użytkownik ustawia stan „Ograniczone” dla wykorzystania baterii w tle w Twojej aplikacji. To ustawienie uniemożliwia aplikacji działanie w tle. Aby to zrobić, uruchom w oknie terminala to polecenie:
  • $ adb shell cmd appops set <PACKAGE_NAME> RUN_ANY_IN_BACKGROUND deny