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:

Jeśli Twoja aplikacja korzysta z któregoś z tych intencji, musisz jak najszybciej usunąć zależności od nich, 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 pozwalają ograniczyć potrzebę stosowania tych niejawnych komunikatów. 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. Po spełnieniu warunków system wykonuje je w zadaniu JobService aplikacji.

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 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 nadmierne zasoby, powiadomi o tym użytkownika i zaoferuje mu możliwość ograniczenia działań aplikacji. Działania, które mogą spowodować wyświetlenie powiadomienia, to między innymi:

  • 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 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 są usuwane z pierwszego planu.
  • Alarmy nie są aktywowane
  • 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 odbierania komunikatów o aktywności w 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, co chcesz osiągnąć dzięki 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 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);
}

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 uruchomiony. WorkManager wybiera odpowiedni sposób wykonania pracy (bezpośrednio w wątku w procesie aplikacji lub za pomocą JobScheduler, FirebaseJobDispatcher lub AlarmManager) na podstawie takich czynników jak poziom interfejsu API urządzenia. Dodatkowo WorkManager nie wymaga Usług Google Play i udostępnia kilka zaawansowanych funkcji, takich jak łączenie zadań ze sobą lub sprawdzanie stanu zadania. 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. Obiekty NetworkRequest tworzysz 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 wideo

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ę zwrotną nawet wtedy, gdy zgłosi zmianę 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 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 zakres JobParameters, aby umożliwić aplikacji otrzymywanie przydatnych informacji o urzędach treści i identyfikatorach URI, które uruchomił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

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 w tle i jawnych odbiorników zarejestrowanych w pliku manifestu może poprawić działanie aplikacji na takich urządzeniach. Chociaż w Androidzie 7.0 (poziom interfejsu API 24) podejmujemy działania, by ograniczyć niektóre z tych problemów, zalecamy zoptymalizowanie działania aplikacji tak, aby działała bez użycia tych procesów w tle.

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

  • 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 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