إدارة طاقة التطبيق

في نظام Android 9 والإصدارات الأحدث، يمكن للنظام مراقبة التطبيقات بحثًا عن السلوك الذي يؤثر سلبًا على عمر بطارية الأجهزة. يستخدم النظام الأساسي قواعد الإعداد ويقيمها لتوفير تدفق تجربة المستخدم الذي يمنح المستخدمين خيار تقييد التطبيقات التي تنتهك القواعد.

في نظام التشغيل Android 8.0 والإصدارات الأحدث، كانت هناك قيود من خلال ميزات مثل Doze ووضع الاستعداد للتطبيق وحدود الخلفية وحدود الموقع في الخلفية. ومع ذلك، استمرت بعض التطبيقات في إظهار سلوكيات سيئة، وقد تم توضيح بعضها في مؤشرات 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 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