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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

setExact()

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

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

setExactAndAllowWhileIdle()

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

setAlarmClock()

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

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

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

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

الإفصاح عن إذن المنبّه الدقيق المناسب

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

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

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

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

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

USE_EXACT_ALARM

SCHEDULE_EXACT_ALARM

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

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

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

استخدام إذن SCHEDULE_EXACT_ALARM

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • ‫intent مؤجّلة يتم تشغيلها عند تفعيل المنبّه عند ضبط منبّه ثانٍ يستخدم intent المؤجّلة نفسها، يحلّ محل المنبّه الأصلي.

لإلغاء 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);
}

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

One of the first considerations in using a repeating alarm is what its type should be.

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

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

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

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

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

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

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

  • ‫RTC: يتم تفعيل الـ intent المؤجّلة في الوقت المحدّد، ولكن لا يتم تنشيط الجهاز.

  • ‫RTC_WAKEUP: لتنشيط الجهاز من أجل تنفيذ الـ intent المؤجّلة في الوقت المحدّد.

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

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

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

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