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

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

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

  • تتيح لك هذه الميزة تشغيل "المقصودات" في أوقات و/أو فواصل زمنية محدّدة.

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

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

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

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

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

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

إرسال تنبيه بعد وقت محدّد

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

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

إرسال تنبيه خلال فترة زمنية

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

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

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

إذا كان تطبيقك يستدعي setInexactRepeating()، يُشغِّل النظام إنذارات متعددة:

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

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

يُشغِّل النظام منبّهًا دقيقًا في لحظة محدّدة في المستقبل.

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

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

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

جدولة عمليات تحديد التوقيت أثناء مدة عمل تطبيقك
تتضمّن فئة Handler عدة methods جيدة لمعالجة عمليات ضبط الوقت، مثل تنفيذ بعض الأعمال كل n ثانية عندما يكون تطبيقك قيد التشغيل: postAtTime() و postDelayed(). يُرجى العِلم أنّ واجهات برمجة التطبيقات هذه تعتمد على وقت عمل النظام وليس الوقت الفعلي.
المهام المجدوَلة التي يتم تنفيذها في الخلفية، مثل تحديث التطبيق وتحميل السجلات
يوفّر
WorkManager طريقة لتحديد موعد لعملٍ دوري حسّاس زمنيًا. يمكنك تقديم فاصل تكرار وflexInterval (15 دقيقة بحد أدنى) لتحديد وقت تشغيل دقيق للعمل.
إجراء يحدّده المستخدم من المفترض أن يحدث بعد وقت محدّد (حتى إذا كان النظام في حالة السكون)
استخدام منبّه غير دقيق يُرجى الاتصال بالرقم التالي: setAndAllowWhileIdle().
إجراء يحدّده المستخدم من المفترض أن يحدث بعد وقت محدّد
استخدام منبّه غير دقيق يُرجى الاتصال بالرقم التالي: set().
إجراء يحدّده المستخدم ويمكن أن يحدث خلال فترة زمنية محدّدة
استخدام منبّه غير دقيق يُرجى الاتصال بالرقم التالي: setWindow(). يُرجى العلم أنّه إذا كان تطبيقك يستهدف الإصدار Android 12 أو إصدارًا أحدث، فإنّ أصغر مدة مسموح بها للنافذة هي 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 لواجهة برمجة التطبيقات) والإصدارات الأحدث. إذا نقل مستخدم بيانات التطبيق إلى جهاز يعمل بنظام التشغيل Android 14 من خلال عملية الاحتفاظ بنسخة احتياطية واستعادتها، سيتم رفض إذن SCHEDULE_EXACT_ALARM على الجهاز الجديد. ومع ذلك، إذا كان تطبيق حالي يمتلك هذا الإذن، سيتم منحه مسبقًا عند upgrade الجهاز إلى 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()، عليك تمرير 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). يستخدِم الوقت الفعلي المنقضي "الوقت منذ بدء تشغيل النظام" كمرجع، ويستخدِم ساعة الوقت الفعلي التوقيت العالمي المنسّق (المعروف أيضًا باسم ساعة الحائط). وهذا يعني أنّ الوقت الفعلي المنقضي مناسب لضبط منبّه استنادًا إلى مرور الوقت (مثل المنبّه الذي يتم تشغيله كل 30 ثانية) لأنّه لا يتأثر بالمنطقة الزمنية أو اللغة. يكون نوع الساعة بالوقت الفعلي أكثر ملاءمةً للمنبهات التي تعتمد على اللغة والمنطقة الحالية.

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

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

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

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

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

  • 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 API، وهي مصمّمة لتنفيذ المهام في الخلفية. يمكنك الإشارة إلى أنّه يجب على النظام تسريع عملك حتى تتمكّن من إكماله في أقرب وقت ممكن. لمزيد من المعلومات، يُرجى الاطّلاع على مقالة جدولة المهام باستخدام WorkManager.

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

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

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

    • تنفيذ أي عمل محلي عند بدء المنبّه "العمل على الجهاز فقط" يعني أي إجراء لا يتطلّب الاتصال بخادم أو طلب البيانات من الخادم.

    • في الوقت نفسه، يمكنك جدولة المنبّه الذي يحتوي على طلبات الشبكة لإطلاقه في فترة زمنية عشوائية.

  • اضبط معدّل تكرار المنبّه على الحد الأدنى.

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

  • لا تضبط وقت تفعيل المنبّه بدقة أكبر من اللازم.

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

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

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