جدولة المنبّهات

توفّر المنبّهات (استنادًا إلى فئة AlarmManager) طريقة لتنفيذ عمليات مستندة إلى الوقت خارج مدة تشغيل تطبيقك. على سبيل المثال، يمكنك استخدام منبّه لبدء عملية طويلة الأمد، مثل بدء خدمة مرة واحدة في اليوم لتنزيل توقّعات الطقس.

تتضمّن المنبّهات الخصائص التالية:

  • تتيح لك هذه الفئة إطلاق Intent في أوقات و/أو فواصل زمنية محدّدة.

  • يمكنك استخدامها مع أدوات استقبال البث لتحديد مواعيد المهام أو WorkRequests لتنفيذ عمليات أخرى.

  • تعمل هذه الخدمات خارج تطبيقك، لذا يمكنك استخدامها لتشغيل أحداث أو إجراءات حتى عندما لا يكون تطبيقك قيد التشغيل، وحتى إذا كان الجهاز نفسه في وضع السكون.

  • تساعدك هذه الميزات في تقليل متطلبات موارد تطبيقك. يمكنك جدولة العمليات بدون الاعتماد على المؤقتات أو الخدمات التي تعمل باستمرار.

ضبط منبّه غير دقيق

عندما يضبط تطبيق منبّه غير دقيق، يرسل النظام المنبّه في وقت ما في المستقبل. تقدّم المنبّهات غير الدقيقة بعض الضمانات بشأن توقيت تسليم المنبّه مع مراعاة القيود المفروضة على توفير البطارية، مثل وضع "توفير الشحن".

يمكن للمطوّرين الاستفادة من ضمانات واجهة برمجة التطبيقات التالية لتخصيص توقيت تسليم المنبّهات غير الدقيقة.

تشغيل منبّه بعد وقت محدّد

إذا كان تطبيقك يستدعي set() أو setInexactRepeating() أو setAndAllowWhileIdle()، لن يرنّ المنبّه أبدًا قبل وقت التشغيل المحدّد.

في نظام التشغيل Android 12 (المستوى 31 من واجهة برمجة التطبيقات) والإصدارات الأحدث، يستدعي النظام المنبّه خلال ساعة واحدة من وقت التشغيل المحدّد، ما لم تكن هناك أي قيود لتوفير شحن البطارية سارية، مثل توفير شحن البطارية أو وضع الاستعداد أثناء الاستخدام.

تشغيل منبّه خلال فترة زمنية

إذا كان تطبيقك يستدعي setWindow()، لن يرنّ المنبّه أبدًا قبل وقت التشغيل المحدّد. ما لم تكن هناك أي قيود لتوفير البطارية سارية، سيتم تسليم المنبّه خلال الفترة الزمنية المحدّدة، بدءًا من وقت التشغيل المحدّد.

إذا كان تطبيقك يستهدف الإصدار 12 من نظام التشغيل Android أو إصدارًا أحدث، يمكن للنظام تأخير استدعاء المنبّه غير الدقيق الذي يتم ضبطه على فترة زمنية محدّدة لمدة 10 دقائق على الأقل. لهذا السبب، يتم اقتطاع قيم المَعلمة windowLengthMillis ضمن 600000 إلى 600000.

عرض منبّه متكرّر على فترات منتظمة تقريبًا

إذا كان تطبيقك يستدعي setInexactRepeating()، سيستدعي النظام تنبيهات متعددة:

  1. ينطلق المنبّه الأول خلال الفترة الزمنية المحدّدة، بدءًا من وقت التشغيل المحدّد.
  2. عادةً ما يتم تشغيل المنبّهات اللاحقة بعد انقضاء الفترة الزمنية المحدّدة. يمكن أن يختلف الوقت بين استدعاءَين متتاليَين للمنبّه.

ضبط منبّه محدَّد الوقت

يُفعِّل النظام منبّهًا دقيقًا في وقت محدّد في المستقبل.

يمكن لمعظم التطبيقات جدولة المهام والأحداث باستخدام المنبّهات غير الدقيقة لتنفيذ العديد من حالات الاستخدام الشائعة. إذا كانت الوظيفة الأساسية لتطبيقك تعتمد على منبّه محدّد الوقت بدقة، مثل تطبيق ساعة منبّه أو تطبيق تقويم، فلا بأس من استخدام منبّه محدّد الوقت بدلاً من ذلك.

حالات الاستخدام التي قد لا تتطلّب منبّهات محدَّدة الوقت

تعرض القائمة التالية سير العمل الشائعة التي قد لا تتطلّب منبّهًا محدَّد الوقت:

جدولة عمليات التوقيت خلال مدة استخدام تطبيقك
يتضمّن الصف Handler عدة طرق جيدة للتعامل مع عمليات التوقيت، مثل تنفيذ بعض المهام كل n ثانية، أثناء تشغيل تطبيقك: postAtTime() و postDelayed(). يُرجى العِلم أنّ واجهات برمجة التطبيقات هذه تعتمد على وقت تشغيل النظام وليس على الوقت الفعلي.
المهام المجدوَلة التي يتم تنفيذها في الخلفية، مثل تحديث تطبيقك وتحميل السجلات
توفّر
WorkManager طريقة لتحديد مواعيد لتنفيذ مهام دورية حساسة زمنيًا. يمكنك تقديم فاصل زمني للتكرار وflexInterval (15 دقيقة كحد أدنى) لتحديد وقت تشغيل دقيق للعمل.
إجراء يحدّده المستخدم ويجب تنفيذه بعد فترة زمنية محدّدة (حتى إذا كان النظام في وضع الخمول)
استخدام منبّه غير دقيق على وجه التحديد، اتّصِل على setAndAllowWhileIdle().
الإجراء الذي يحدّده المستخدم والذي من المفترض أن يحدث بعد وقت معيّن
استخدام منبّه غير دقيق على وجه التحديد، اتّصِل على set().
إجراء يحدّده المستخدم ويمكن أن يحدث خلال فترة زمنية محدّدة
استخدام منبّه غير دقيق على وجه التحديد، اتّصِل على setWindow(). يُرجى العِلم أنّه إذا كان تطبيقك يستهدف الإصدار 12 من نظام التشغيل Android أو إصدارًا أحدث، فإنّ أقصر مدة زمنية مسموح بها هي 10 دقائق.

طُرق ضبط منبّه محدَّد الوقت

يمكن لتطبيقك ضبط المنبّهات المحددة الوقت باستخدام إحدى الطرق التالية. يتم ترتيب هذه الطرق بحيث تخدم الطرق الأقرب إلى أسفل القائمة المهام الأكثر أهمية من حيث الوقت، ولكنها تتطلب المزيد من موارد النظام.

setExact()

تفعيل منبّه في وقت دقيق تقريبًا في المستقبل، ما دام لم يتم تفعيل أي إجراءات أخرى للحفاظ على البطارية

استخدِم هذه الطريقة لضبط المنبّهات المحددة الوقت، ما لم يكن عمل تطبيقك مهمًا للمستخدم في الوقت الفعلي.

setExactAndAllowWhileIdle()

تفعيل منبّه في وقت دقيق تقريبًا في المستقبل، حتى إذا كانت إجراءات توفير شحن البطارية مفعَّلة

setAlarmClock()

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

استهلاك موارد النظام

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

ننصحك بشدة بإنشاء منبّه غير دقيق كلما أمكن ذلك. لتنفيذ مهام أطول، يمكنك جدولتها باستخدام WorkManager أو JobScheduler من BroadcastReceiver. لتنفيذ عمل أثناء وضع "توفير الطاقة"، أنشئ منبّهًا غير محدَّد الوقت باستخدام setAndAllowWhileIdle()، وابدأ مهمة من المنبّه.

تضمين بيان إذن المنبّهات المحدَّدة الوقت المناسب

إذا كان تطبيقك يستهدف الإصدار 12 من نظام التشغيل Android أو الإصدارات الأحدث، يجب الحصول على إذن "المنبّهات والتذكيرات" من خلال أذونات الوصول الخاصة للتطبيقات. لإجراء ذلك، يجب إدراج الإذن SCHEDULE_EXACT_ALARM في ملف البيان الخاص بتطبيقك، كما هو موضّح في مقتطف الرمز التالي:

<manifest ...>
    <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
    <application ...>
        ...
    </application>
</manifest>

إذا كان تطبيقك يستهدف الإصدار 13 من نظام التشغيل Android (المستوى 33 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، يمكنك اختيار الإفصاح عن إذن SCHEDULE_EXACT_ALARM أو إذن USE_EXACT_ALARM.

<manifest ...>
    <uses-permission android:name="android.permission.USE_EXACT_ALARM"/>
    <application ...>
        ...
    </application>
</manifest>

على الرغم من أنّ الإذنَين SCHEDULE_EXACT_ALARM وUSE_EXACT_ALARM يشيران إلى الإمكانات نفسها، إلا أنّهما يُمنحان بشكل مختلف ويتيحان حالات استخدام مختلفة. يجب أن يستخدم تطبيقك المنبّهات المحدَّدة الوقت وأن يفصح عن استخدام الإذن SCHEDULE_EXACT_ALARM أو USE_EXACT_ALARM فقط إذا كانت إحدى الوظائف الموجَّهة للمستخدمين في تطبيقك تتطلّب إجراءات محدّدة الوقت بدقة.

USE_EXACT_ALARM

SCHEDULE_EXACT_ALARM

  • منحها المستخدم
  • مجموعة أوسع من حالات الاستخدام
  • على التطبيقات التأكّد من عدم إبطال الإذن

لا يتم منح الإذن SCHEDULE_EXACT_ALARM مسبقًا لعمليات التثبيت الجديدة للتطبيقات التي تستهدف الإصدار 13 من نظام التشغيل Android (المستوى 33 لواجهة برمجة التطبيقات) والإصدارات الأحدث. إذا نقل المستخدم بيانات التطبيق إلى جهاز يعمل بالإصدار 14 من نظام التشغيل Android من خلال عملية النسخ الاحتياطي والاستعادة، سيتم رفض إذن SCHEDULE_EXACT_ALARM على الجهاز الجديد. ومع ذلك، إذا كان تطبيق حالي لديه هذا الإذن، سيتم منحه مسبقًا عند ترقية الجهاز إلى الإصدار Android 14.

ملاحظة: إذا تم ضبط المنبّه المحدّد الوقت باستخدام عنصر OnAlarmListener، مثل واجهة برمجة التطبيقات setExact، لا يكون الإذن SCHEDULE_EXACT_ALARM مطلوبًا.

استخدام إذن SCHEDULE_EXACT_ALARM

على عكس إذن USE_EXACT_ALARM، يجب أن يمنح المستخدم إذن SCHEDULE_EXACT_ALARM. يمكن للمستخدم والنظام إبطال إذن SCHEDULE_EXACT_ALARM.

للتحقّق مما إذا كان الإذن قد تم منحه لتطبيقك، استدعِ الدالة canScheduleExactAlarms() قبل محاولة ضبط منبّه محدّد الوقت. عند إبطال الإذن SCHEDULE_EXACT_ALARM لتطبيقك، سيتوقف تطبيقك وسيتم إلغاء جميع المنبّهات المحدّدة الوقت المستقبلية. يعني هذا أيضًا أنّ القيمة التي تعرضها الدالة canScheduleExactAlarms() تظل صالحة طوال فترة نشاط تطبيقك.

عند منح تطبيقك الإذن SCHEDULE_EXACT_ALARMS، يرسل النظام البث ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED إلى التطبيق. يجب أن ينفّذ تطبيقك أداة استقبال البث التي تنفّذ ما يلي:

  1. يؤكّد هذا الإذن أنّ تطبيقك لا يزال لديه إذن الوصول الخاص إلى التطبيقات. لإجراء ذلك، اتّصِل بالرقم canScheduleExactAlarms(). يحمي هذا الإجراء تطبيقك من الحالات التي يمنح فيها المستخدم تطبيقك الإذن، ثم يلغيه بعد ذلك مباشرةً.
  2. يعيد جدولة أي منبّهات محدّدة الوقت يحتاجها تطبيقك، وذلك استنادًا إلى حالته الحالية. يجب أن تكون هذه العملية مشابهة لما يفعله تطبيقك عند تلقّي ACTION_BOOT_COMPLETED.

طلب الإذن SCHEDULE_EXACT_ALARM من المستخدمين

يُسمّى هذا الخيار &quot;السماح بضبط المنبّهات والتذكيرات&quot;
الشكل 1. صفحة "أذونات خاصة للتطبيقات" الخاصة بـ "المنبّهات والتذكيرات" في إعدادات النظام، حيث يمكن للمستخدمين السماح لتطبيقك بضبط منبّهات دقيقة

يمكنك عند الضرورة توجيه المستخدمين إلى شاشة المنبّهات والتذكيرات في إعدادات النظام، كما هو موضّح في الشكل 1. لإجراء ذلك، يُرجى إكمال الخطوات التالية:

  1. في واجهة المستخدم لتطبيقك، اشرح للمستخدم سبب حاجة تطبيقك إلى جدولة المنبّهات الدقيقة.
  2. استدعاء هدف يتضمّن إجراء الهدف ACTION_REQUEST_SCHEDULE_EXACT_ALARM.

ضبط منبّه متكرّر

تسمح التنبيهات المتكررة للنظام بإرسال إشعارات إلى تطبيقك وفقًا لجدول زمني متكرر.

يمكن أن يتسبب المنبّه المصمَّم بشكل سيئ في استنزاف البطارية وفرض عبء كبير على الخوادم. لهذا السبب، في الإصدار 4.4 من نظام التشغيل Android (المستوى 19 لواجهة برمجة التطبيقات) والإصدارات الأحدث، تكون جميع المنبّهات المتكرّرة منبّهات غير دقيقة.

يتميّز المنبّه المتكرّر بالخصائص التالية:

  • نوع التنبيه لمزيد من المناقشة، يُرجى الاطّلاع على اختيار نوع المنبّه.

  • وقت التشغيل إذا كان وقت التشغيل الذي تحدّده في الماضي، سيتم تشغيل المنبّه على الفور.

  • الفاصل الزمني للمنبّه على سبيل المثال، مرة واحدة في اليوم أو كل ساعة أو كل 5 دقائق.

  • كائن PendingIntent يتم تنشيطه عند تشغيل المنبّه. عند ضبط منبّه ثانٍ يستخدم الغرض المعلّق نفسه، سيحلّ محل المنبّه الأصلي.

لإلغاء PendingIntent()، مرِّر FLAG_NO_CREATE إلى PendingIntent.getService() للحصول على مثيل للغرض (إذا كان متوفّرًا)، ثم مرِّر هذا الغرض إلى AlarmManager.cancel().

Kotlin

val alarmManager =
    context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
val pendingIntent =
    PendingIntent.getService(context, requestId, intent,
                                PendingIntent.FLAG_NO_CREATE)
if (pendingIntent != null && alarmManager != null) {
  alarmManager.cancel(pendingIntent)
}

Java

AlarmManager alarmManager =
    (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent =
    PendingIntent.getService(context, requestId, intent,
                                PendingIntent.FLAG_NO_CREATE);
if (pendingIntent != null && alarmManager != null) {
  alarmManager.cancel(pendingIntent);
}

اختيار نوع المنبّه

من أول الاعتبارات عند استخدام تنبيه متكرر هو تحديد نوع التنبيه.

هناك نوعان عامان من الساعات للمنبهات: "الوقت الفعلي المنقضي" و"ساعة الوقت الفعلي" (RTC). يستخدم الوقت الفعلي المنقضي "الوقت منذ بدء تشغيل النظام" كمرجع، بينما يستخدم ساعة الوقت الفعلي التوقيت العالمي المنسق (UTC) (ساعة الحائط). وهذا يعني أنّ الوقت الفعلي المنقضي مناسب لضبط منبّه استنادًا إلى مرور الوقت (على سبيل المثال، منبّه يرنّ كل 30 ثانية) لأنّه لا يتأثر بالمنطقة الزمنية أو اللغة. يكون نوع ساعة الوقت الفعلي أنسب للمنبهات التي تعتمد على اللغة الحالية.

يتضمّن كلا النوعين إصدارًا "للتنشيط"، وهو يطلب تنشيط وحدة المعالجة المركزية (CPU) في الجهاز إذا كانت الشاشة مطفأة، ما يضمن تشغيل المنبّه في الوقت المحدّد. يكون ذلك مفيدًا إذا كان تطبيقك يعتمد على الوقت. على سبيل المثال، إذا كان لديه فترة زمنية محدودة لتنفيذ عملية معيّنة. إذا لم تستخدِم إصدار الاستيقاظ من نوع المنبّه، سيتم تشغيل جميع المنبّهات المتكرّرة عند تنشيط جهازك في المرة التالية.

إذا كنت تحتاج فقط إلى تنشيط المنبّه على فواصل زمنية معيّنة (على سبيل المثال، كل نصف ساعة)، استخدِم أحد أنواع الوقت المنقضي الفعلي. وبشكل عام، هذا هو الخيار الأفضل.

إذا كنت بحاجة إلى أن يرنّ المنبّه في وقت معيّن من اليوم، اختَر أحد أنواع الساعات المستندة إلى الساعة في الوقت الفعلي. يُرجى العِلم، مع ذلك، أنّ هذه الطريقة قد تتضمّن بعض السلبيات. وقد لا يتوافق التطبيق بشكل جيد مع اللغات الأخرى، وإذا غيّر المستخدم إعدادات الوقت على الجهاز، قد يؤدي ذلك إلى حدوث سلوك غير متوقّع في تطبيقك. كما أنّ استخدام نوع تنبيه ساعة الوقت الفعلي لا يتوافق بشكل جيد مع التغييرات، كما هو موضّح أعلاه. ننصحك باستخدام منبّه "الوقت الفعلي المنقضي" إذا كان ذلك ممكنًا.

في ما يلي قائمة بالأنواع:

  • ELAPSED_REALTIME: يتم تشغيل PendingIntent استنادًا إلى مقدار الوقت المنقضي منذ بدء تشغيل الجهاز، ولكن بدون تنبيه الجهاز. يتضمّن الوقت المنقضي أي وقت كان فيه الجهاز في وضع السكون.

  • ELAPSED_REALTIME_WAKEUP: يوقظ الجهاز ويُطلق النية المعلّقة بعد انقضاء المدة الزمنية المحدّدة منذ بدء تشغيل الجهاز.

  • RTC: يتم تشغيل النية المعلّقة في الوقت المحدّد بدون تنبيه الجهاز.

  • RTC_WAKEUP: يؤدي إلى تنبيه الجهاز لتفعيل النية المعلّقة في الوقت المحدّد.

أمثلة على المنبّهات التي تستند إلى الوقت المنقضي

في ما يلي بعض الأمثلة على استخدام ELAPSED_REALTIME_WAKEUP

تفعيل المنبّه بعد 30 دقيقة، وكل 30 دقيقة بعد ذلك:

Kotlin

// Hopefully your alarm will have a lower frequency than this!
alarmMgr?.setInexactRepeating(
        AlarmManager.ELAPSED_REALTIME_WAKEUP,
        SystemClock.elapsedRealtime() + AlarmManager.INTERVAL_HALF_HOUR,
        AlarmManager.INTERVAL_HALF_HOUR,
        alarmIntent
)

Java

// Hopefully your alarm will have a lower frequency than this!
alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
        SystemClock.elapsedRealtime() + AlarmManager.INTERVAL_HALF_HOUR,
        AlarmManager.INTERVAL_HALF_HOUR, alarmIntent);

تنشيط الجهاز لتشغيل منبّه لمرة واحدة (غير متكرّر) بعد دقيقة واحدة:

Kotlin

private var alarmMgr: AlarmManager? = null
private lateinit var alarmIntent: PendingIntent
...
alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmIntent = Intent(context, AlarmReceiver::class.java).let { intent ->
    PendingIntent.getBroadcast(context, 0, intent, 0)
}

alarmMgr?.set(
        AlarmManager.ELAPSED_REALTIME_WAKEUP,
        SystemClock.elapsedRealtime() + 60 * 1000,
        alarmIntent
)

Java

private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
...
alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);

alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
        SystemClock.elapsedRealtime() +
        60 * 1000, alarmIntent);

أمثلة على تنبيهات الساعة في الوقت الفعلي

في ما يلي بعض الأمثلة على استخدام RTC_WAKEUP.

إيقاظ الجهاز لتشغيل المنبّه في الساعة 2:00 ظهرًا تقريبًا، وتكراره مرة واحدة يوميًا في الوقت نفسه:

Kotlin

// Set the alarm to start at approximately 2:00 p.m.
val calendar: Calendar = Calendar.getInstance().apply {
    timeInMillis = System.currentTimeMillis()
    set(Calendar.HOUR_OF_DAY, 14)
}

// With setInexactRepeating(), you have to use one of the AlarmManager interval
// constants--in this case, AlarmManager.INTERVAL_DAY.
alarmMgr?.setInexactRepeating(
        AlarmManager.RTC_WAKEUP,
        calendar.timeInMillis,
        AlarmManager.INTERVAL_DAY,
        alarmIntent
)

Java

// Set the alarm to start at approximately 2:00 p.m.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 14);

// With setInexactRepeating(), you have to use one of the AlarmManager interval
// constants--in this case, AlarmManager.INTERVAL_DAY.
alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
        AlarmManager.INTERVAL_DAY, alarmIntent);

إيقاظ الجهاز لتشغيل المنبّه في تمام الساعة 8:30 صباحًا، وكل 20 دقيقة بعد ذلك:

Kotlin

private var alarmMgr: AlarmManager? = null
private lateinit var alarmIntent: PendingIntent
...
alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmIntent = Intent(context, AlarmReceiver::class.java).let { intent ->
    PendingIntent.getBroadcast(context, 0, intent, 0)
}

// Set the alarm to start at 8:30 a.m.
val calendar: Calendar = Calendar.getInstance().apply {
    timeInMillis = System.currentTimeMillis()
    set(Calendar.HOUR_OF_DAY, 8)
    set(Calendar.MINUTE, 30)
}

// setRepeating() lets you specify a precise custom interval--in this case,
// 20 minutes.
alarmMgr?.setRepeating(
        AlarmManager.RTC_WAKEUP,
        calendar.timeInMillis,
        1000 * 60 * 20,
        alarmIntent
)

Java

private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
...
alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);

// Set the alarm to start at 8:30 a.m.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 30);

// setRepeating() lets you specify a precise custom interval--in this case,
// 20 minutes.
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
        1000 * 60 * 20, alarmIntent);

تحديد مدى الدقة المطلوبة للمنبه

كما سبق أن ذكرنا، غالبًا ما يكون اختيار نوع المنبّه هو الخطوة الأولى في إنشاء منبّه. يتمثل الفرق الآخر في مدى دقة المنبّه الذي تحتاجه. بالنسبة إلى معظم التطبيقات، setInexactRepeating() هو الخيار المناسب. عند استخدام هذه الطريقة، يزامن نظام التشغيل Android عدة تنبيهات متكررة غير دقيقة ويطلقها في الوقت نفسه. يؤدي ذلك إلى تقليل استهلاك البطارية.

تجنَّب استخدام المنبّهات المحدَّدة الوقت قدر الإمكان. ومع ذلك، بالنسبة إلى التطبيقات النادرة التي تتضمّن متطلبات وقت صارمة، يمكنك ضبط منبّه دقيق من خلال استدعاء setRepeating().

باستخدام setInexactRepeating()، لا يمكنك تحديد فاصل زمني مخصّص كما يمكنك باستخدام setRepeating(). عليك استخدام أحد ثوابت الفاصل الزمني، مثل INTERVAL_FIFTEEN_MINUTES وINTERVAL_DAY وما إلى ذلك. يمكنك الاطّلاع على AlarmManager للحصول على القائمة الكاملة.

إلغاء منبّه

بناءً على تطبيقك، قد تحتاج إلى تضمين إمكانية إلغاء التنبيه. لإلغاء منبّه، اتّصِل بالرقم cancel() في Alarm Manager، مع تمرير PendingIntent الذي لم يعُد مطلوبًا تشغيله. مثلاً:

Kotlin

// If the alarm has been set, cancel it.
alarmMgr?.cancel(alarmIntent)

Java

// If the alarm has been set, cancel it.
if (alarmMgr!= null) {
    alarmMgr.cancel(alarmIntent);
}

بدء منبّه عند إعادة تشغيل الجهاز

يتم تلقائيًا إلغاء جميع المنبّهات عند إيقاف تشغيل الجهاز. لمنع حدوث ذلك، يمكنك تصميم تطبيقك لإعادة تشغيل المنبّه المتكرّر تلقائيًا إذا أعاد المستخدم تشغيل الجهاز. يضمن ذلك استمرار AlarmManager في تنفيذ مهمته بدون أن يحتاج المستخدم إلى إعادة تشغيل المنبّه يدويًا.

إليك الخطوات التي يمكنك اتّباعها:

  1. اضبط إذن RECEIVE_BOOT_COMPLETED في ملف بيان التطبيق. يسمح هذا الإذن لتطبيقك بتلقّي ACTION_BOOT_COMPLETED الذي يتم بثّه بعد انتهاء النظام من عملية بدء التشغيل (لا يعمل هذا الإذن إلا إذا سبق للمستخدم تشغيل التطبيق مرة واحدة على الأقل):

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
  2. نفِّذ BroadcastReceiver لتلقّي البث:

    Kotlin

    class SampleBootReceiver : BroadcastReceiver() {
    
        override fun onReceive(context: Context, intent: Intent) {
            if (intent.action == "android.intent.action.BOOT_COMPLETED") {
                // Set the alarm here.
            }
        }
    }

    Java

    public class SampleBootReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
                // Set the alarm here.
            }
        }
    }
  3. أضِف أداة الاستقبال إلى ملف البيان الخاص بتطبيقك مع فلتر أهداف يفلتر حسب إجراء ACTION_BOOT_COMPLETED:

    <receiver android:name=".SampleBootReceiver"
            android:enabled="false">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"></action>
        </intent-filter>
    </receiver>

    لاحظ أنّه في ملف البيان، تم ضبط مستلِم عملية التشغيل على android:enabled="false". وهذا يعني أنّه لن يتم استدعاء المتلقّي إلا إذا فعّله التطبيق بشكل صريح. ويمنع ذلك استدعاء أداة استقبال عملية التشغيل بدون داعٍ. يمكنك تفعيل أداة استقبال (على سبيل المثال، إذا ضبط المستخدم منبّهًا) على النحو التالي:

    Kotlin

    val receiver = ComponentName(context, SampleBootReceiver::class.java)
    
    context.packageManager.setComponentEnabledSetting(
            receiver,
            PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
            PackageManager.DONT_KILL_APP
    )

    Java

    ComponentName receiver = new ComponentName(context, SampleBootReceiver.class);
    PackageManager pm = context.getPackageManager();
    
    pm.setComponentEnabledSetting(receiver,
            PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
            PackageManager.DONT_KILL_APP);

    بعد تفعيل جهاز الاستقبال بهذه الطريقة، سيظل مفعّلاً حتى إذا أعاد المستخدم تشغيل الجهاز. بعبارة أخرى، يؤدي تفعيل أداة الاستقبال آليًا إلى تجاهل إعدادات ملف البيان، حتى بعد إعادة التشغيل. سيظل جهاز الاستقبال مفعّلاً إلى أن يوقفه تطبيقك. يمكنك إيقاف جهاز استقبال (على سبيل المثال، إذا ألغى المستخدم منبهًا) على النحو التالي:

    Kotlin

    val receiver = ComponentName(context, SampleBootReceiver::class.java)
    
    context.packageManager.setComponentEnabledSetting(
            receiver,
            PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
            PackageManager.DONT_KILL_APP
    )

    Java

    ComponentName receiver = new ComponentName(context, SampleBootReceiver.class);
    PackageManager pm = context.getPackageManager();
    
    pm.setComponentEnabledSetting(receiver,
            PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
            PackageManager.DONT_KILL_APP);

تفعيل المنبّهات عندما يكون الجهاز في وضع "توفير الطاقة أثناء عدم الاستخدام"

تتيح الأجهزة التي تعمل بالإصدار 6.0 من نظام التشغيل Android (المستوى 23 من واجهة برمجة التطبيقات) استخدام وضع الاستراحة الذي يساعد في إطالة عمر بطارية الجهاز. لا يتم تشغيل المنبّهات عندما يكون الجهاز في وضع "السكون" يتم تأجيل أي منبّهات مُجدوَلة إلى أن يخرج الجهاز من وضع "السكون". إذا كنت بحاجة إلى إكمال العمل حتى عندما يكون الجهاز غير نشط، تتوفّر لك عدة خيارات:

  • ضبط منبّه دقيق

  • استخدِم واجهة برمجة التطبيقات WorkManager المصمَّمة لتنفيذ العمل في الخلفية. يمكنك الإشارة إلى أنّ النظام يجب أن يسرّع عملك حتى ينتهي في أقرب وقت ممكن. لمزيد من المعلومات، يمكنك الاطّلاع على جدولة المهام باستخدام WorkManager.

أفضل الممارسات

يمكن أن يكون لكل خيار تتخذه في تصميم المنبّه المتكرّر عواقب في طريقة استخدام تطبيقك لموارد النظام (أو إساءة استخدامها). على سبيل المثال، تخيَّل تطبيقًا شائعًا تتم مزامنته مع خادم. إذا كانت عملية المزامنة تستند إلى الوقت الفعلي، وكانت كل نسخة من التطبيق تتم مزامنتها في الساعة 11:00 مساءً، قد يؤدي الحمل على الخادم إلى زيادة وقت الاستجابة أو حتى "رفض الخدمة". اتّبِع أفضل الممارسات التالية عند استخدام المنبّهات:

  • إضافة عشوائية (تشويش) إلى أي طلبات شبكة يتم تشغيلها نتيجة لمنبّه متكرر:

    • تنفيذ أي عمل محلي عند تشغيل المنبّه يشير "العمل المحلي" إلى أي عملية لا تتطلب الوصول إلى خادم أو الحصول على بيانات من الخادم.

    • في الوقت نفسه، جدوِل المنبّه الذي يتضمّن طلبات الشبكة ليتم تشغيله خلال فترة زمنية عشوائية.

  • حاوِل تقليل معدّل تكرار التنبيهات إلى الحدّ الأدنى.

  • تجنَّب تنبيه الجهاز بدون داعٍ (يتم تحديد هذا السلوك حسب نوع المنبّه، كما هو موضّح في اختيار نوع المنبّه).

  • لا تجعل وقت تشغيل المنبّه أكثر دقة مما يجب.

    استخدِم setInexactRepeating() بدلاً من setRepeating(). عند استخدام setInexactRepeating()، يزامن نظام التشغيل Android الإنذارات المتكررة من تطبيقات متعددة ويطلقها في الوقت نفسه. يؤدي ذلك إلى تقليل إجمالي عدد المرات التي يجب أن يوقظ فيها النظام الجهاز، وبالتالي تقليل استهلاك البطارية. اعتبارًا من الإصدار 4.4 من نظام التشغيل Android (المستوى 19 لواجهة برمجة التطبيقات)، أصبحت جميع المنبّهات المتكرّرة منبّهات غير دقيقة. يُرجى العِلم أنّه على الرغم من أنّ setInexactRepeating() يُعدّ تحسينًا مقارنةً setRepeating()، إلّا أنّه قد يؤدي إلى إرهاق الخادم إذا وصل إليه عدد كبير من طلبات مثيلات التطبيق في الوقت نفسه تقريبًا. لذلك، بالنسبة إلى طلبات الشبكة، أضِف بعض العشوائية إلى التنبيهات، كما هو موضّح سابقًا.

  • تجنَّب ضبط المنبّه على وقت الساعة إذا أمكن ذلك.

    لا يمكن توسيع نطاق المنبّهات المتكرّرة التي تستند إلى وقت تفعيل دقيق بشكل جيد. استخدِم ELAPSED_REALTIME إذا كان ذلك ممكنًا. يتم وصف أنواع التنبيهات المختلفة بمزيد من التفصيل في القسم التالي.