Zarządzanie zużyciem energii przez aplikacje

W Androidzie 9 i nowszych platforma może monitorować aplikacje pod kątem zachowań, które negatywnie wpływają na czas pracy urządzenia na baterii. Platforma używa i wycenia reguły konfiguracji, aby zapewnić przepływ UX, który daje użytkownikom możliwość ograniczania dostępu do aplikacji, które naruszają te reguły.

W systemie Android 8.0 i starszych obowiązywały ograniczenia związane z funkcjami takimi jak Doze, tryb czuwania aplikacji, limity w tle i ograniczenia dostępu do lokalizacji w tle. Niektóre aplikacje nadal jednak wykazywały niewłaściwe działanie, a niektóre z nich są opisane w Android Vitals. Android 9 wprowadził infrastrukturę systemu operacyjnego, która może wykrywać aplikacje i ograniczać ich działanie na podstawie reguł konfiguracji, które można aktualizować w czasie.

Ograniczenia dotyczące działania w tle

Użytkownicy mogą ograniczać dostęp do aplikacji, a system może sugerować aplikacje, które według niego negatywnie wpływają na stan urządzenia.

Aplikacje z ograniczeniami:

  • nadal może być uruchamiana przez użytkownika;
  • Nie można uruchamiać zadań ani alarmów ani korzystać z sieci w tle.
  • Nie można uruchamiać usług na pierwszym planie.
  • Użytkownik może zmienić aplikację na nieograniczoną.

Implementatorzy urządzeń mogą dodawać dodatkowe ograniczenia dla aplikacji, aby:

  • ograniczyć możliwość ponownego uruchamiania aplikacji przez nią samą.
  • Ograniczenie możliwości wiązania usług (bardzo ryzykowne).

Ograniczone aplikacje działające w tle nie powinny zużywać żadnych zasobów urządzenia, takich jak pamięć, procesor czy bateria. Aplikacje z ograniczeniami w tle nie powinny wpływać na stan urządzenia, gdy użytkownik nie korzysta z nich aktywnie. Te same aplikacje powinny jednak działać prawidłowo, gdy użytkownik je uruchomi.

Korzystanie z implementacji niestandardowych

Implementatorzy urządzeń mogą nadal stosować własne metody do nakładania ograniczeń na aplikacje.

Integracja ograniczeń aplikacji

W następnych sekcjach znajdziesz informacje o tym, jak definiować i integrować ograniczenia aplikacji na urządzeniu. Jeśli używasz metod ograniczania dostępu do aplikacji w Androidzie 8.x lub starszym, dokładnie zapoznaj się z poniższymi sekcjami, aby poznać zmiany w Androidzie 9 i nowszych.

Ustawianie flagi AppOpsManager

Gdy aplikacja jest ograniczona, ustaw odpowiednią flagę w pliku AppOpsManager. Przykładowy fragment kodu z packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryUtils.java:

   public void setForceAppStandby(int uid, String packageName,
            int mode) {
        final boolean isPreOApp = isPreOApp(packageName);
        if (isPreOApp) {
       // Control whether app could run in the background if it is pre O app
            mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName, mode);
        }
       // Control whether app could run jobs in the background
        mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName, mode);
    }

Upewnij się, że zwracana wartość isBackgroundRestricted jest prawdziwa.

Gdy dostęp do aplikacji jest ograniczony, sprawdź, czy ActivityManager.isBackgroundRestricted() zwraca true.

Zarejestruj powód ograniczenia

Gdy aplikacja jest ograniczona, odnotuj przyczyny tego ograniczenia. Przykładowy fragment kodu logowania z packages/apps/Settings/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppAction.java:

mBatteryUtils.setForceAppStandby(mBatteryUtils.getPackageUid(packageName), packageName,AppOpsManager.MODE_IGNORED);
if (CollectionUtils.isEmpty(appInfo.anomalyTypes)) {
  // Only log context if there is no anomaly type
  mMetricsFeatureProvider.action(mContext,
    MetricsProto.MetricsEvent.ACTION_TIP_RESTRICT_APP, packageName,
    Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT,metricsKey));
            } else {
  // Log ALL the anomaly types
  for (int type : appInfo.anomalyTypes) {
    mMetricsFeatureProvider.action(mContext,
      MetricsProto.MetricsEvent.ACTION_TIP_RESTRICT_APP, packageName,
      Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, metricsKey),
      Pair.create(MetricsProto.MetricsEvent.FIELD_ANOMALY_TYPE, type));
  }

Zastąp type wartością z AnomalyType.

Implementatorzy urządzeń mogą używać stałych wartości zdefiniowanych w :src/com/android/settings/fuelgauge/batterytip/StatsManagerConfig.java:

public @interface AnomalyType {
        // This represents an error condition in the anomaly detection.
        int NULL = -1;
         // The anomaly type does not match any other defined type.
        int UNKNOWN_REASON = 0;
         // The application held a partial (screen off) wake lock for a period of time that
         // exceeded the threshold with the screen off when not charging.
        int EXCESSIVE_WAKELOCK_ALL_SCREEN_OFF = 1;
         // The application exceeded the maximum number of wakeups while in the background
         // when not charging.
        int EXCESSIVE_WAKEUPS_IN_BACKGROUND = 2;
         // The application did unoptimized Bluetooth scans too frequently when not charging.
        int EXCESSIVE_UNOPTIMIZED_BLE_SCAN = 3;
         // The application ran in the background for a period of time that exceeded the
         // threshold.
        int EXCESSIVE_BACKGROUND_SERVICE = 4;
         // The application exceeded the maximum number of wifi scans when not charging.
        int EXCESSIVE_WIFI_SCAN = 5;
         // The application exceed the maximum number of flash writes
        int EXCESSIVE_FLASH_WRITES = 6;
         // The application used more than the maximum memory, while not spending any time
         // in the foreground.
        int EXCESSIVE_MEMORY_IN_BACKGROUND = 7;
         // The application exceeded the maximum percentage of frames with a render rate of
         // greater than 700ms.
        int EXCESSIVE_DAVEY_RATE = 8;
         // The application exceeded the maximum percentage of frames with a render rate
         // greater than 16ms.
        int EXCESSIVE_JANKY_FRAMES = 9;
         // The application exceeded the maximum cold start time - the app has not been
         // launched since last system start, died or was killed.
        int SLOW_COLD_START_TIME = 10;
         // The application exceeded the maximum hot start time - the app and activity are
         // already in memory.
        int SLOW_HOT_START_TIME = 11;
         // The application exceeded the maximum warm start time - the app was already in
         // memory but the activity wasn't created yet or was removed from memory.
        int SLOW_WARM_START_TIME = 12;
         // The application exceeded the maximum number of syncs while in the background.
        int EXCESSIVE_BACKGROUND_SYNCS = 13;
         // The application exceeded the maximum number of gps scans while in the background.
        int EXCESSIVE_GPS_SCANS_IN_BACKGROUND = 14;
         // The application scheduled more than the maximum number of jobs while not charging.
        int EXCESSIVE_JOB_SCHEDULING = 15;
         // The application exceeded the maximum amount of mobile network traffic while in
         // the background.
        int EXCESSIVE_MOBILE_NETWORK_IN_BACKGROUND = 16;
         // The application held the WiFi lock for more than the maximum amount of time while
         // not charging.
        int EXCESSIVE_WIFI_LOCK_TIME = 17;
         // The application scheduled a job that ran longer than the maximum amount of time.
        int JOB_TIMED_OUT = 18;
         // The application did an unoptimized Bluetooth scan that exceeded the maximum
         // time while in the background.
        int LONG_UNOPTIMIZED_BLE_SCAN = 19;
         // The application exceeded the maximum ANR rate while in the background.
        int BACKGROUND_ANR = 20;
         // The application exceeded the maximum crash rate while in the background.
        int BACKGROUND_CRASH_RATE = 21;
         // The application exceeded the maximum ANR-looping rate.
        int EXCESSIVE_ANR_LOOPING = 22;
         // The application exceeded the maximum ANR rate.
        int EXCESSIVE_ANRS = 23;
         // The application exceeded the maximum crash rate.
        int EXCESSIVE_CRASH_RATE = 24;
         // The application exceeded the maximum crash-looping rate.
        int EXCESSIVE_CRASH_LOOPING = 25;
         // The application crashed because no more file descriptors were available.
        int NUMBER_OF_OPEN_FILES = 26;
    }

Jeśli użytkownik lub system usunie ograniczenia aplikacji, musisz zarejestrować powody ich usunięcia. Przykładowy fragment kodu logowania z packages/apps/Settings/src/com/android/settings/fuelgauge/batterytip/actions/UnrestrictAppAction.java:

public void handlePositiveAction(int metricsKey) {
        final AppInfo appInfo = mUnRestrictAppTip.getUnrestrictAppInfo();
        // Clear force app standby, then app can run in the background
        mBatteryUtils.setForceAppStandby(appInfo.uid, appInfo.packageName,
                AppOpsManager.MODE_ALLOWED);
        mMetricsFeatureProvider.action(mContext,
                MetricsProto.MetricsEvent.ACTION_TIP_UNRESTRICT_APP, appInfo.packageName,
                Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, metricsKey));
    }

Testowanie ograniczeń aplikacji

Aby przetestować działanie ograniczeń aplikacji w Androidzie 9 i nowszych wersjach, użyj jednego z tych poleceń:

  • Wprowadzanie ograniczeń dla aplikacji:
    appops set package-name RUN_ANY_IN_BACKGROUND ignore
  • Aby odblokować aplikację i przywrócić jej działanie domyślne:
    appops set package-name RUN_ANY_IN_BACKGROUND allow
  • Aby aplikacja działająca w tle natychmiast przeszła w stan bezczynności:
    am make-uid-idle [--user user-id | all | current] package-name
  • Dodaj pakiet do folderu tempwhitelist na krótki czas:
    cmd deviceidle tempwhitelist [-u user] [-d duration] [package package-name]
  • Dodawanie i usuwanie pakietu z białej listy użytkownika:
    cmd deviceidle whitelist [+/-]package-name
  • Sprawdź wewnętrzny stan jobscheduler i menedżera alarmów:
    dumpsys jobscheduler
    dumpsys alarm

Czuwanie aplikacji

Tryb gotowości aplikacji wydłuża czas pracy na baterii, opóźniając aktywność w sieci i zadania w tle w przypadku aplikacji, których użytkownik nie używa aktywnie.

Cykl życia aplikacji w trybie czuwania

Platforma wykrywa nieaktywne aplikacje i przenosi je do trybu gotowości, dopóki użytkownik nie zacznie z nich aktywnie korzystać.

fazie wykrywania platforma wykrywa, że aplikacja jest nieaktywna, gdy urządzenie nie jest ładowane i użytkownik nie uruchomił jej bezpośrednio ani pośrednio przez określony czas. (Uruchomienia pośrednie występują, gdy aplikacja na pierwszym planie uzyskuje dostęp do usługi w innej aplikacji).

Podczas czasu oczekiwania aplikacji platforma uniemożliwia aplikacjom dostęp do sieci częściej niż raz dziennie, opóźniając synchronizację aplikacji i inne zadania.

Platforma wywołuje aplikację z trybu gotowości, gdy:

  • Aplikacja stanie się aktywna.
  • Urządzenie jest podłączone i właśnie się ładuje.

Tryb gotowości aplikacji nie ma wpływu na aktywne aplikacje. Aplikacja jest aktywna, gdy:

  • proces, który jest obecnie na pierwszym planie (czyli jako aktywność lub usługa na pierwszym planie albo używany przez inną aktywność lub usługę na pierwszym planie), na przykład odbiornik powiadomień, usługi ułatwień dostępu, tapeta na żywo itp.
  • Powiadomienie wyświetlane przez użytkownika, np. na ekranie blokady lub w obszarze powiadomień
  • zostało wyraźnie uruchomione przez użytkownika;

Aplikacja jest nieaktywna, jeśli przez pewien czas nie wystąpiła żadna z wymienionych powyżej czynności.

Testowanie czuwania aplikacji

Możesz ręcznie przetestować tryb czuwania aplikacji za pomocą tych poleceń:adb

adb shell dumpsys battery unplug
adb shell am set-idle package-name true
adb shell am set-idle package-name false
adb shell am get-idle package-name