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

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

تشمل المنبّهات السمات التالية:

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

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

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

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

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

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

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

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

إذا استدعى تطبيقك 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>

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

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

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

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

    • القيام بأي عمل محلي عند تشغيل المنبه يعني "العمل المحلي" أي شيء لا يصل إلى أي خادم أو يتطلب البيانات من الخادم.

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

  • حافظ على الحد الأدنى لمعدل تكرار المنبه.

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

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

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

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

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