در Android نسخه ۹ و بالاتر، پلاتفرم میتواند برنامهها را ازنظر رفتاری که بر عمر باتری دستگاهها تأثیر منفی میگذارد پایش کند. پلاتفرم از قوانین راهاندازی استفاده میکند و آنها را ارزیابی میکند تا جریان تجربه کاربریای ارائه دهد که به کاربران امکان میدهد برنامههایی را که قوانین را نقض میکنند محدود کنند.
در Android 8.0 و نسخههای پایینتر، محدودیتهایی ازطریق ویژگیهایی مانند «چرت زدن»، «حالت آماده بهکار برنامه»، «محدودیتهای پسزمینه»، و «محدودیتهای مکان پسزمینهای» وجود داشت. بااینحال، برخیاز برنامهها همچنان رفتارهای بدی از خود نشان دادند که برخیاز آنها در معیارهای کلیدی Android شرح داده شده است. Android 9 زیرساخت سیستمعاملی را معرفی کرد که میتواند برنامهها را براساس قوانین راهاندازی که میتوانند درطول زمان بهروز شوند شناسایی و محدود کند.
محدودیتهای پسزمینه
کاربران میتوانند برنامهها را محدود کنند، یا سیستم ممکن است برنامههایی را پیشنهاد دهد که تشخیص میدهد تأثیر منفی بر سلامت دستگاه دارند.
برنامههای محدودشده:
- کاربر همچنان بتواند آن را راهاندازی کند.
- نمیتواند کارها/زنگهای ساعت را اجرا کند یا از شبکه در پسزمینه استفاده کند.
- سرویسهای پیشزمینهای اجرا نمیشود.
- کاربر میتواند آن را به برنامه بدون محدودیت تغییر دهد.
پیادهسازان دستگاه میتوانند محدودیتهای بیشتری برای برنامهها اضافه کنند تا:
- برنامه را از بازراهاندازی خودکار محدود کنید.
- محدود کردن سرویسها از محدود شدن (بسیار پرخطر).
انتظار نمیرود برنامههای محدودشده در پسزمینه هیچیک از منابع دستگاه را مصرف کنند، ازجمله حافظه، واحد پردازش مرکزی، و باتری. وقتی کاربر بهطور فعال از برنامههای محدودشده در پسزمینه استفاده نمیکند، این برنامهها نباید بر سلامت دستگاه تأثیر بگذارند. بااینحال، انتظار میرود وقتی کاربر برنامهها را راهاندازی میکند، همان برنامهها کاملاً کاربردی باشند.
استفاده از پیادهسازیهای سفارشی
پیادهسازان دستگاه میتوانند همچنان از روشهای سفارشی خود برای اعمال محدودیت روی برنامهها استفاده کنند.
ادغام محدودیتهای برنامه
بخشهای زیر نحوه تعریف و ادغام محدودیتهای برنامه در دستگاه را شرح میدهد. اگر از روشهای محدودسازی برنامه در 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 مقدار درست را برمیگرداند
وقتی برنامهای محدود میشود، مطمئن شوید که
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 jobschedulerdumpsys alarm
حالت آماده بهکار برنامه
«آمادهبهکار برنامه» با بهتعویق انداختن فعالیت و کارهای شبکه پسزمینه برای برنامههایی که کاربر بهطور فعال از آنها استفاده نمیکند، عمر باتری را افزایش میدهد.
چرخه حیات آمادهبهکار برنامه
پلاتفرم برنامههای غیرفعال را شناسایی میکند و آنها را در حالت آمادهبهکار برنامه قرار میدهد تا زمانی که کاربر بهطور فعال با برنامه تعامل برقرار کند.
درطول مرحله تشخیص، پلاتفرم تشخیص میدهد که برنامه غیرفعال است وقتی دستگاه درحال شارژ نیست و کاربر برنامه را بهطور مستقیم یا غیرمستقیم برای مدتزمان مشخصی از ساعت و همچنین مدتزمان مشخصی از روشن بودن صفحهنمایش راهاندازی نکرده است. (راهاندازیهای غیرمستقیم زمانی رخ میدهد که برنامه پیشزمینهای به سرویسی در برنامه دوم دسترسی پیدا میکند.)
درطول حالت آمادهبهکار برنامه، پلاتفرم از دسترسی برنامهها به شبکه بیشاز یک بار در روز جلوگیری میکند و همگامسازی برنامه و کارهای دیگر را بهتعویق میاندازد.
پلاتفرم زمانی از برنامه در حالت آمادهبهکار خارج میشود که:
- برنامه فعال شود.
- دستگاه به برق متصل است و شارژ میشود.
برنامههای فعال تحتتأثیر حالت آمادهبهکار برنامه قرار نمیگیرند. برنامه زمانی فعال است که:
- فرایندی که درحالحاضر در پیشزمینه است (بهعنوان فعالیت یا سرویس پیشزمینهای، یا درحال استفاده توسط فعالیت یا سرویس پیشزمینهای دیگر)، مثل شنونده اعلان، خدمات دسترسپذیری، کاغذدیواری پویا، و غیره.
- اعلانی که کاربر مشاهده کرده است، مثلاً در صفحه قفل یا سینی اعلان
- بهطور صریح توسط کاربر راهاندازی شده باشد
برنامه درصورتی غیرفعال است که هیچیک از فعالیتهای بالا در یک دوره زمانی رخ نداده باشد.
آزمایش آمادهبهکار برنامه
میتوانید بااستفاده از فرمانهای زیر، برنامه درانتظار را بهصورت دستی آزمایش کنید: adb
adb shell dumpsys battery unplugadb shell am set-idle package-name trueadb shell am set-idle package-name falseadb shell am get-idle package-name