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

تمنحك المنبّهات (استنادًا إلى الفئة 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(). يُرجى العلم أنّه إذا كان تطبيقك يستهدف الإصدار 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>

إذا كان تطبيقك يستهدف 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 مسبقًا لعمليات التثبيت الجديدة ل التطبيقات التي تستهدف الإصدار 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

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

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

يمكن أن يؤدي استخدام منبّه غير صائب إلى استنزاف البطارية ووضع عبء زائد على الخوادم. لهذا السبب، في 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);

استدعِ المنبّهات عندما يكون الجهاز في وضع "القيلولة"

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

  • اضبط منبّهًا دقيقًا.

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

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

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

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

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

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

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

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

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

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

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

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