Optymalizacja w tle

Procesy w tle mogą pochłaniać dużo pamięci i baterii. Na przykład komunikat pośredni może uruchomić wiele procesów w tle, które zarejestrowały go w celu nasłuchiwania, nawet jeśli te procesy nie przyniosą zamierzonego efektu. Może to mieć znaczny wpływ na wydajność urządzenia i wrażenia użytkownika.

Aby rozwiązać ten problem, Android 7.0 (poziom interfejsu API 24) stosuje 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 nowa wersja WorkManager zapewniają niezawodne mechanizmy do planowania operacji sieciowych, gdy są spełnione określone warunki, takie jak połączenie z siecią bez pomiaru. Teraz możesz też używać JobScheduler, aby reagować na zmiany dotyczące dostawców treści. Obiekty JobInfo zawierają parametry, których JobScheduler używa do planowania zadania. Po spełnieniu warunków zadania system wykonuje je w zadaniu JobService aplikacji.

Na tej stronie dowiesz się, jak używać metod alternatywnych, 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ń: zezwala na pracę w tle, co może zużywać więcej baterii.
  • Zoptymalizowana (ustawienie domyślne): optymalizacja działania aplikacji w tle na podstawie sposobu interakcji użytkownika z aplikacją.
  • Z ograniczonym dostępem:całkowicie uniemożliwia aplikacji działanie w tle. Aplikacje mogą nie działać zgodnie z oczekiwaniami.

Jeśli aplikacja wykazuje pewne działania opisane w Android Vitals, system może poprosić użytkownika o ograniczenie jej dostępu 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ń. Działania, które mogą spowodować wyświetlenie powiadomienia, to między innymi:

  • Nadmierne blokady uśpienia: jedna częściowa blokada uśpienia zatrzymana na godzinę przy wyłączonym ekranie
  • Nadmierne usługi w tle: jeśli aplikacja jest kierowana na poziomy interfejsu API niższe niż 26 i zawiera nadmierną liczbę 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ą 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.

Konkretne ograniczenia znajdziesz w artykule Ograniczenia dotyczące zarządzania energią.

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 zarejestrują się, aby je otrzymywać w pliku manifestu, a procesy zależne od tej transmisji nie zostaną uruchomione. Może to stanowić problem w przypadku aplikacji, które chcą nasłuchiwać zmian w sieci lub wykonywać masowe działania w sieci, gdy urządzenie łączy się z siecią bez pomiaru. 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 komunikaty, gdy aplikacja jest uruchomiona.

Zaplanuj zadania sieciowe w przypadku połączeń bez pomiaru użycia danych

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 planuje uruchomienie 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);
}

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 funkcji JobScheduler znajdziesz w przykładowej aplikacji JoobScheduler.

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 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 zadań. Więcej informacji znajdziesz w sekcji WorkManager.

Monitorowanie połączeń sieciowych podczas działania aplikacji

Włączone aplikacje mogą nadal nasłuchiwać przez CONNECTIVITY_CHANGE przy zarejestrowanej wartości 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 postaci NetworkCapabilities. Obiekty NetworkRequest tworzysz za pomocą klasy NetworkRequest.Builder. Następnie registerNetworkCallback() przekazuje obiekt NetworkRequest do systemu. W przypadku spełnienia warunków sieciowych aplikacja otrzymuje wywołanie zwrotne do wykonania metody onAvailable() zdefiniowanej w jej 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 zmniejsza wydajność i wygodę użytkowników, gdy kilka aplikacji musi się wybudzić w celu przetworzenia nowego obrazu lub filmu. Android 7.0 (poziom interfejsu API 24) rozszerza funkcje JobInfo i JobParameters, aby zapewnić alternatywne rozwiązanie.

Aktywuj zadania w przypadku zmian identyfikatora URI treści

Aby aktywować zadania po zmianie 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()
Przekazuje obiekt TriggerContentUri do JobInfo. ContentObserver monitoruje zawarty w nim 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 flagę TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS, aby aktywować zadanie, jeśli zmienią się jakiekolwiek elementy podrzędne danego identyfikatora URI. Ta flaga odpowiada parametrowi notifyForDescendants przekazanemu do registerContentObserver().

Uwaga: TriggerContentUri() nie można używać w połączeniu z właściwościami setPeriodic() ani setPersisted(). Aby stale monitorować zmiany treści, zaplanuj nowy element JobInfo, zanim JobService aplikacji zakończy obsługę ostatniego wywołania zwrotnego.

Ten przykładowy kod planuje aktywowanie 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ę określonych identyfikatorów URI treści, aplikacja otrzyma wywołanie zwrotne i obiekt JobParameters zostanie przekazany do metody onStartJob() w MediaContentJob.class.

Sprawdzanie, które organizacje zajmujące się treścią aktywował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 aktywowały zadanie. Wartość null oznacza, że zadanie nie zostało aktywowane za pomocą żadnych identyfikatorów URI (na przykład ze względu na termin lub z innego powodu) albo liczba zmienionych identyfikatorów URI przekracza 50.
String[] getTriggeredContentAuthorities()
Zwraca tablicę z ciągami znaków urzędów treści, które aktywowały zadanie. Jeśli zwrócona tablica ma wartość inną niż null, użyj getTriggeredContentUris(), aby pobrać szczegółowe informacje o zmienionych identyfikatorach 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 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ć użytkownika, który włącza aplikację w trybie „ograniczony” w celu wykorzystania 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