Optymalizacja w tle

Procesy w tle mogą pochłaniać dużo pamięci i baterii. 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ć komunikaty z kategorii CONNECTIVITY_ACTION, jeśli zarejestrują swoją BroadcastReceiver w Context.registerReceiver() i ten kontekst jest nadal 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 danych. 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 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:

  • Bez ograniczeń: zezwala na pracę w tle, co może powodować wyższe wykorzystanie baterii.
  • Zoptymalizowana (domyślnie): optymalizuje zdolność aplikacji do wykonywania zadań w tle na podstawie sposobu 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 zbyt dużo zasobów, powiadamia o tym użytkownika i umożliwia mu ograniczenie jego działań. 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ą wywoływane
  • Zadania nie są wykonywane

Poza tym, jeśli aplikacja jest kierowana na Androida 13 (poziom interfejsu API 33) lub nowszego i jest w stanie „ograniczonym”, system nie wyświetli 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 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 łą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 do tworzenia obiektu JobInfo używasz klasy JobInfo.Builder, zastosuj metodę setRequiredNetworkType() i przekaż JobInfo.NETWORK_TYPE_UNMETERED jako parametr zadania. Poniższy przykładowy kod umożliwia zaplanowanie uruchomienia usługi, gdy urządzenie połączy się z siecią bez pomiaru użycia 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);
}

Po spełnieniu warunków zadania aplikacja otrzyma wywołanie zwrotne zachęcające do uruchomienia metody 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 interfejs API WorkManager umożliwiający planowanie zadań w tle, które wymagają gwarantowanego ukończenia, niezależnie od tego, czy proces aplikacji jest uruchomiony. 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 też 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 spełnione są 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 obrazów i transmisji wideo

Na Androidzie 7.0 (poziom interfejsu API 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.

Aktywuj zadania w przypadku zmian 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 aktywowania 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 wykona wywołanie zwrotne, nawet jeśli zgłosi zmianę tylko w jednym 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 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. Wartość to null, jeśli żaden identyfikator URI nie wywołał zadania (na przykład zadanie zostało aktywowane ze względu na termin lub z innego powodu) lub liczba zmienionych identyfikatoró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ć użytkownika, który włącza aplikację w trybie „ograniczony” w celu wykorzystania baterii w tle. To ustawienie uniemożliwia aplikacji działanie 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