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

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

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

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

  • يمكنك استخدامها مع أجهزة استقبال البث لجدولة المهام أو 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. لتنفيذ العمل أثناء وضع الجهاز في Doze، يمكنك إنشاء إنذار غير دقيق باستخدام setAndAllowWhileIdle()، وبدء مهمة من المنبّه.

تعريف الإذن المناسب للإنذار بالتحديد

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

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

إذا كان تطبيقك يستهدف نظام التشغيل Android 13 (المستوى 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"

على عكس 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.

تعيين تنبيه متكرر

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

يمكن أن يتسبب الإنذار السيئ التصميم إلى استنزاف البطارية وتحميل الخادم عبئًا كبيرًا. لهذا السبب، تكون جميع المنبّهات المتكرّرة منبهات غير محدّدة في نظام التشغيل Android 4.4 (المستوى 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). يستخدم "الوقت الفعلي المنقضي" "الوقت منذ تشغيل النظام" كمرجع، في حين تستخدم الساعة في الوقت الفعلي التوقيت العالمي المتفق عليه (UTC) (ساعة جدارية). وهذا يعني أنّ "الوقت الفعلي المنقضي" مناسب لضبط إنذار بناءً على مرور الوقت (على سبيل المثال، منبه ينشط كل 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() بـ "مدير المنبّهات"، واضبط 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 المنبّهات المتكررة من تطبيقات متعددة ويطلقها في الوقت نفسه. يقلل هذا من العدد الإجمالي للمرات التي يجب أن ينشط فيها النظام الجهاز، مما يقلل من استنزاف البطارية. بدايةً من Android 4.4 (المستوى 19 من واجهة برمجة التطبيقات)، تكون جميع المنبّهات المتكرّرة منبهات غير محددة. يُرجى العلم أنّه على الرغم من أنّ الترميز setInexactRepeating() يمثّل تحسّنًا مقارنةً بـ setRepeating()، لا يزال بإمكانه إرباك الخادم إذا وصلت كل نسخة من التطبيقات إلى الخادم في الوقت نفسه تقريبًا. لذلك، بالنسبة لطلبات الشبكة، يجب إضافة بعض العشوائية إلى المنبّهات، كما أشرنا سابقًا.

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

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