Optymalizacja w tle

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:

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 do JobInfo. ContentObserver monitoruje identyfikator URI treści w zamkniętej treści. Jeśli z zadaniem jest powiązanych wiele obiektów TriggerContentUri, 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 parametrowi notifyForDescendants przekazanemu do registerContentObserver().

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 funkcji getTriggeredContentUris(), 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