תזמון התראות

התראות (מבוססות על המחלקה AlarmManager) מאפשרות לבצע פעולות מבוססות-זמן מחוץ למשך החיים של האפליקציה. לדוגמה, אפשר להשתמש בהתראה כדי להתחיל פעולה ממושכת, כמו הפעלת שירות פעם ביום כדי להוריד תחזית מזג אוויר.

המאפיינים של התראות:

  • הם מאפשרים להפעיל Intent בזמנים או במרווחי זמן קבועים.

  • אתם יכולים להשתמש בהם בשילוב עם מקלטי שידור כדי לתזמן משימות או WorkRequests לביצוע פעולות אחרות.

  • הם פועלים מחוץ לאפליקציה, כך שאפשר להשתמש בהם כדי להפעיל אירועים או פעולות גם כשהאפליקציה לא פועלת, ואפילו אם המכשיר עצמו במצב שינה.

  • הם עוזרים לכם לצמצם את דרישות המשאבים של האפליקציה. אתם יכולים לתזמן פעולות בלי להסתמך על טיימרים או על שירותים שפועלים באופן רציף.

הגדרת התראה לא מדויקת

כשאפליקציה מגדירה התראה לא מדויקת, המערכת מפעילה את ההתראה בנקודת זמן כלשהי בעתיד. התראות לא מדויקות מספקות הבטחות מסוימות לגבי התזמון של שליחת ההתראה, תוך כיבוד ההגבלות לחיסכון בסוללה כמו Doze.

המפתחים יכולים להשתמש בהתחייבויות הבאות של ה-API כדי להתאים אישית את התזמון של שליחת התראות לא מדויקות.

התראה אחרי שעה מסוימת

אם האפליקציה מפעילה את set(), setInexactRepeating(), או setAndAllowWhileIdle(), ההתראה אף פעם לא מופעלת לפני זמן הטריגר שסופק.

ב-Android 12 (רמת API‏ 31) ואילך, המערכת מפעילה את ההתראה תוך שעה מזמן הטריגר שסופק, אלא אם חלות הגבלות כלשהן לחיסכון בסוללה, כמו חיסכון בסוללה או Doze.

הפעלת התראה בחלון זמן מסוים

אם האפליקציה מפעילה את setWindow(), ההתראה אף פעם לא מופעלת לפני זמן ההפעלה שצוין. אם לא חלות הגבלות לחיסכון בסוללה, ההתראה מופעלת בחלון הזמן שצוין, החל מהזמן שנקבע להפעלה.

אם האפליקציה מטרגטת את Android 12 ואילך, המערכת יכולה לעכב את ההפעלה של התראה לא מדויקת עם חלון זמן ב-10 דקות לפחות. לכן, ערכי הפרמטרים של windowLengthMillis שקטנים מ-600000 בדרך כלל נחתכים ל-600000. המערכת עשויה להתאים חלונות קטנים יותר, במיוחד אם ההתראה מתוזמנת להפעלה בעתיד הקרוב.

הפעלת התראה חוזרת במרווחי זמן קבועים פחות או יותר

אם האפליקציה קוראת ל-setInexactRepeating(), המערכת מפעילה כמה התראות:

  1. ההתראה הראשונה מופעלת בחלון הזמן שצוין, החל משעת ההפעלה שצוינה.
  2. ההתראות הבאות בדרך כלל מופעלות אחרי שחלון הזמן שצוין חולף. הזמן בין שתי הפעלות רצופות של ההתראה יכול להשתנות.

הגדרת משימה ברגע מדויק

המערכת מפעילה משימה ברגע מדויק בעתיד.

ברוב האפליקציות אפשר לתזמן משימות ואירועים באמצעות התראות לא מדויקות כדי להשלים כמה תרחישים נפוצים לדוגמה. אם הפונקציונליות העיקרית של האפליקציה תלויה בהתראה שמופעלת בזמן מדויק, כמו באפליקציה של שעון מעורר או של יומן, אפשר להשתמש בהתראה מדויקת.

תרחישים לדוגמה שבהם לא בטוח שצריך להשתמש במשימות ברגע מדויק

ברשימה הבאה מפורטים תהליכי עבודה נפוצים שלא בהכרח דורשים משימה ברגע מדויק:

תזמון פעולות במהלך חיי האפליקציה
הכיתה Handler כוללת כמה שיטות טובות לטיפול בפעולות תזמון, כמו ביצוע עבודה כל n שניות, בזמן שהאפליקציה פועלת: postAtTime() ו-postDelayed(). חשוב לזכור שממשקי ה-API האלה מסתמכים על זמן הפעולה התקינה של המערכת ולא על זמן אמת.
עבודה מתוזמנת ברקע, כמו עדכון האפליקציה והעלאת יומנים
‫
WorkManager מאפשר לתזמן עבודה תקופתית שרגישה לזמן. אפשר לציין מרווח זמן לחזרה וflexInterval (מינימום 15 דקות) כדי להגדיר זמן ריצה מפורט לעבודה.
פעולה שהמשתמש ציין שצריכה לקרות אחרי זמן מסוים (גם אם המערכת במצב חוסר פעילות)
משתמשים בהתראה לא מדויקת. באופן ספציפי, שולחים קריאה אל setAndAllowWhileIdle().
פעולה שהמשתמש ציין שצריכה לקרות אחרי זמן מסוים
משתמשים בהתראה לא מדויקת. באופן ספציפי, שולחים קריאה אל set().
פעולה שהמשתמש ציין שיכולה להתרחש בתוך חלון זמן מסוים
משתמשים בהתראה לא מדויקת. באופן ספציפי, צריך לשלוח קריאה אל setWindow(). שימו לב: אם האפליקציה מטרגטת את Android מגרסה 12 ואילך, משך הזמן המינימלי המותר לחלון הוא 10 דקות.

איך מגדירים משימה ברגע מדויק

האפליקציה יכולה להגדיר משימות ברגע מדויק באחת מהשיטות הבאות. השיטות האלה מסודרות כך שהשיטות שקרובות יותר לתחתית הרשימה משרתות משימות קריטיות יותר מבחינת זמן, אבל דורשות יותר משאבי מערכת.

setExact()

הפעלת התראה בזמן כמעט מדויק בעתיד, כל עוד לא מופעלים אמצעים אחרים לחיסכון בסוללה.

השתמשו בשיטה הזו כדי להגדיר משימות ברגע מדויק, אלא אם העבודה של האפליקציה היא קריטית מבחינת הזמן למשתמש.

setExactAndAllowWhileIdle()

להפעיל התראה בזמן כמעט מדויק בעתיד, גם אם אמצעי החיסכון בסוללה פועלים.

setAlarmClock()

הפעלת התראה בשעה מדויקת בעתיד. ההתראות האלה בולטות מאוד למשתמשים, ולכן המערכת אף פעם לא משנה את זמן ההצגה שלהן. המערכת מזהה את ההתראות האלה כהתראות הכי קריטיות, ויוצאת ממצבי חיסכון בסוללה אם צריך כדי להציג את ההתראות.

צריכת משאבי מערכת

כשהמערכת מפעילה משימות ברגע מדויק שהאפליקציה מגדירה, המכשיר צורך הרבה משאבים, כמו חיי סוללה, במיוחד אם הוא במצב חיסכון בסוללה. בנוסף, המערכת לא יכולה לאסוף את הבקשות האלה בקלות כדי להשתמש במשאבים בצורה יעילה יותר.

מומלץ מאוד ליצור התראה לא מדויקת בכל פעם שאפשר. כדי לבצע עבודה ארוכה יותר, צריך לתזמן אותה באמצעות WorkManager או JobScheduler מה-BroadcastReceiver של ההתראה. כדי לבצע עבודה כשהמכשיר במצב Doze, צריך ליצור התראה לא מדויקת באמצעות setAndAllowWhileIdle() ולהתחיל משימה מההתראה.

הצהרה על ההרשאה המתאימה למשימה ברגע מדויק

אם האפליקציה שלכם מטרגטת את Android מגרסה 12 ואילך, אתם צריכים לקבל את הרשאת הגישה המיוחדת "התראות ותזכורות". כדי לעשות את זה, צריך להצהיר על ההרשאה SCHEDULE_EXACT_ALARM בקובץ המניפסט של האפליקציה, כמו שמוצג בקטע הקוד הבא:

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

אם האפליקציה מטרגטת ל-Android 13 (רמת API‏ 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 (רמת API‏ 33) ומעלה. אם משתמש מעביר נתוני אפליקציה למכשיר עם Android בגרסה 14 באמצעות פעולת גיבוי ושחזור, ההרשאה SCHEDULE_EXACT_ALARM תידחה במכשיר החדש. עם זאת, אם לאפליקציה קיימת כבר יש את ההרשאה הזו, היא תוענק מראש כשהמכשיר ישודרג ל-Android 14.

הערה: אם המשימה ברגע מדויק מוגדרת באמצעות אובייקט OnAlarmListener, למשל באמצעות ה-API‏ 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. מפעילים Intent שכולל את פעולת ה-Intent‏ ACTION_REQUEST_SCHEDULE_EXACT_ALARM.

הגדרת שעון מעורר חוזר

התראות חוזרות מאפשרות למערכת להודיע לאפליקציה שלכם לפי לוח זמנים קבוע.

התראה שלא תוכננה כראוי עלולה לרוקן את הסוללה ולהעמיס על השרתים. לכן, ב-Android מגרסה 4.4 (רמת API‏ 19) ואילך, כל ההתראות החוזרות הן התראות לא מדויקות.

אלו המאפיינים של התראה חוזרת:

  • סוג התראה. למידע נוסף, ראו בחירת סוג התראה.

  • זמן הפעלה. אם זמן ההפעלה שציינתם הוא בעבר, ההתראה מופעלת מיד.

  • מרווח הזמן של ההתראה. לדוגמה, פעם ביום, כל שעה או כל 5 דקות.

  • intent בהמתנה שמופעלת כשההתראה מופעלת. כשמגדירים התראה שנייה שמשתמשת באותה intent בהמתנה, היא מחליפה את ההתראה המקורית.

כדי לבטל PendingIntent(), מעבירים את FLAG_NO_CREATE אל PendingIntent.getService() כדי לקבל מופע של ה-Intent (אם הוא קיים), ואז מעבירים את ה-Intent הזה אל 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 שניות), כי הוא לא מושפע מאזור הזמן או מהלוקאל. סוג השעון בזמן אמת מתאים יותר להתראות שתלויות במיקום הנוכחי.

לשני הסוגים יש גרסת "התעוררות", שאומרת להעיר את המעבד של המכשיר אם המסך כבוי. כך אפשר לוודא שההתראה תופעל בזמן המתוזמן. האפשרות הזו שימושית אם האפליקציה שלכם תלויה בזמן. לדוגמה, אם יש לו חלון זמן מוגבל לביצוע פעולה מסוימת. אם לא משתמשים בגרסת ההתעוררות של סוג ההתראה, כל ההתראות החוזרות יופעלו בפעם הבאה שהמכשיר יתעורר.

אם אתם רק צריכים שההתראה תופעל במרווח זמן מסוים (לדוגמה, כל חצי שעה), השתמשו באחד מהסוגים של הזמן שחלף בפועל. באופן כללי, זו האפשרות הטובה יותר.

אם אתם צריכים שההתראה תופעל בשעה מסוימת ביום, בחרו באחד מסוגי השעונים שמבוססים על שעון זמן אמת. עם זאת, חשוב לזכור שלגישה הזו יש כמה חסרונות. יכול להיות שהאפליקציה לא תתורגם היטב לאזורים אחרים, ואם המשתמש ישנה את הגדרת הזמן של המכשיר, זה עלול לגרום להתנהגות בלתי צפויה באפליקציה. שימוש בסוג התראה של שעון זמן אמת גם לא מתאים לשינוי קנה מידה, כפי שצוין למעלה. מומלץ להשתמש בהתראה מסוג "elapsed real time" אם אפשר.

אלה סוגי המודעות:

  • ‫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.

הפעלת המכשיר כדי להפעיל את ההתראה בסביבות השעה 14: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 שמסנן לפי הפעולה 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);

הפעלת התראות כשהמכשיר במצב Doze

מכשירים עם Android בגרסה 6.0 (רמת API‏ 23) תומכים במצב Doze, שעוזר להאריך את חיי הסוללה של המכשיר. ההתראות לא מופעלות כשהמכשיר נמצא במצב חיסכון בסוללה. כל ההתראות המתוזמנות נדחות עד שהמכשיר יוצא ממצב חיסכון בסוללה. אם אתם צריכים להשלים עבודה גם כשהמכשיר לא פעיל, יש כמה אפשרויות:

שיטות מומלצות

כל בחירה שתעשו בעיצוב ההתראה החוזרת יכולה להשפיע על האופן שבו האפליקציה משתמשת במשאבי המערכת (או מנצלת אותם לרעה). לדוגמה, נניח שיש אפליקציה פופולרית שמסתנכרנת עם שרת. אם פעולת הסנכרון מבוססת על זמן שעון וכל מכונה של האפליקציה מסתנכרנת בשעה 23:00, העומס על השרת עלול לגרום לזמן טעינה ארוך או אפילו ל"התקפת מניעת שירות (DoS)". כדאי לפעול לפי השיטות המומלצות הבאות לשימוש בהתראות:

  • הוסיפו אקראיות (רעידות) לכל בקשה לאחזור מהרשת שמופעלת כתוצאה מהתראה חוזרת:

    • לבצע כל פעולה מקומית כשההתראה מופעלת. "עבודה מקומית" היא כל פעולה שלא מגיעה לשרת או לא דורשת נתונים מהשרת.

    • במקביל, מתזמנים את ההתראה שמכילה את הבקשות לאחזור מהרשת כך שתופעל בפרק זמן אקראי.

  • הגדירו את תדירות ההתראות למינימום.

  • לא להעיר את המכשיר שלא לצורך (ההתנהגות הזו נקבעת לפי סוג ההתראה, כפי שמתואר בקטע בחירת סוג התראה).

  • אל תגדירו את זמן ההפעלה של ההתראה בצורה מדויקת יותר ממה שצריך.

    משתמשים ב-setInexactRepeating() במקום ב-setRepeating(). כשמשתמשים ב-setInexactRepeating(), מערכת Android מסנכרנת התראות חוזרות מכמה אפליקציות ומפעילה אותן בו-זמנית. כך המערכת צריכה להעיר את המכשיר פחות פעמים, וצריכת הסוללה פוחתת. החל מ-Android 4.4 (רמת API‏ 19), כל ההתראות החוזרות הן התראות לא מדויקות. שימו לב: למרות ש-setInexactRepeating() הוא שיפור לעומת setRepeating(), הוא עדיין יכול להעמיס על השרת אם כל מופע של אפליקציה פונה לשרת בערך באותו זמן. לכן, בבקשות לאחזור מהרשת, הוסיפו קצת אקראיות להתראות, כפי שצוין קודם.

  • אם אפשר, כדאי להימנע מהגדרת התראה לפי השעה בשעון.

    שעונים מעוררים חוזרים שמבוססים על זמן הפעלה מדויק לא מתאימים לשימוש נרחב. אם אפשר, השתמשו ב-ELAPSED_REALTIME. בקטע הבא מפורטים סוגי ההתראות השונים.