在 Android 9 及更高版本中,該平台可以監控應用程式是否有對裝置電池壽命產生負面影響的行為。該平台使用並評估設定規則來提供使用者體驗流程,使用戶可以選擇限制違反規則的應用程式。
在 Android 8.0 及更低版本中,透過 Doze、應用程式待機、後台限制和後台位置限制等功能進行了限制。然而,一些應用程式繼續表現出不良行為,其中一些在Android Vitals中進行了描述。 Android 9 引入了一個作業系統基礎架構,可以根據可隨時間更新的設定規則來偵測和限制應用程式。
背景限制
用戶可以限制應用程序,或者係統可能會建議它檢測到的對設備運行狀況產生負面影響的應用程式。
受限制的應用程式:
- 仍然可以由使用者啟動。
- 無法在背景執行作業/警報或使用網路。
- 無法運作前台服務。
- 用戶可以更改為不受限制的應用程式。
設備實施者可以為應用程式添加額外的限制:
- 限制應用程式自行重新啟動。
- 限制服務被綁定(風險較高)。
後台受限制的應用程式不會消耗任何裝置資源,例如記憶體、CPU 和電池。當使用者未主動使用背景限制應用程式時,這些應用程式不應影響裝置運作狀況。但是,當用戶啟動應用程式時,相同的應用程式預計將具有完整的功能。
使用自訂實現
設備實現者可以繼續使用他們的自訂方法對應用程式應用限制。
整合應用程式限制
以下部分概述如何在裝置上定義和整合應用程式限制。如果您使用的是 Android 8.x 或更低版本的應用程式限制方法,請仔細查看以下部分,以了解 Android 9 及更高版本中的變更。
設定 AppOpsManager 標誌
當應用程式受到限制時,請在AppOpsManager
中設定適當的標誌。來自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); }
確保 isBackgroundRestricted 傳回 true
當應用程式受到限制時,請確保ActivityManager.isBackgroundRestricted()
傳回true
。
記錄限制原因
當應用程式受到限制時,記錄限制原因。來自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)); }
將type
替換為AnomalyType
中的值。
裝置實作者可以使用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; }
當使用者或系統刪除應用程式的限制時,您必須記錄刪除限制的原因。來自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)); }
測試應用程式限制
若要測試 Android 9 及更高版本中套用限制的行為,請使用下列其中一個指令:
- 將應用程式置於限制中:
appops set package-name RUN_ANY_IN_BACKGROUND ignore
- 使應用程式擺脫限制並恢復預設行為:
appops set package-name RUN_ANY_IN_BACKGROUND allow
- 讓後台應用程式立即空閒:
am make-uid-idle [--user user-id | all | current] package-name
- 將包包短期加入
tempwhitelist
:cmd deviceidle tempwhitelist [-u user] [-d duration] [package package-name]
- 從使用者白名單中新增/移除套件:
cmd deviceidle whitelist [+/-]package-name
- 檢查
jobscheduler
和警報管理器的內部狀態:dumpsys jobscheduler
dumpsys alarm
應用程式待機
應用程式待機可透過延遲使用者不常使用的應用程式的背景網路活動和作業來延長電池壽命。
應用待機生命週期
該平台會偵測不活動的應用程式並將其置於應用程式待機狀態,直到用戶開始積極使用該應用程式。
在偵測階段,當裝置未充電且使用者在特定的時鐘時間以及特定的螢幕開啟時間內沒有直接或間接啟動應用程式時,平台會偵測到應用程式處於非活動狀態。 (當前台應用程式存取第二個應用程式中的服務時,會發生間接啟動。)
在應用程式待機期間,平台會阻止應用程式每天多次存取網絡,從而推遲應用程式同步和其他作業。
在以下情況下,平台會使應用程式退出待機狀態:
- 該應用程式變得活躍。
- 設備已插入電源並正在充電。
活動應用程式不受應用程式待機的影響。應用程式在滿足以下條件時處於活動狀態:
- 目前處於前台的進程(作為活動或前台服務,或由另一個活動或前台服務使用),例如通知偵聽器、輔助服務、動態桌布等。
- 使用者查看的通知,例如在鎖定螢幕或通知托盤中
- 由使用者明確啟動
如果一段時間內沒有發生上述任何活動,則應用程式處於非活動狀態。
測試應用待機狀況
您可以使用以下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