ตั้งเวลาปลุก

การปลุก (อิงตามคลาส AlarmManager) ช่วยให้คุณดำเนินการตามเวลานอกอายุการใช้งานของแอปพลิเคชันได้ เช่น คุณอาจใช้การปลุกเพื่อเริ่มการดำเนินการที่ทำงานต่อเนื่องเป็นเวลานาน เช่น การเริ่มบริการวันละครั้งเพื่อดาวน์โหลดพยากรณ์อากาศ

สัญญาณเตือนมีลักษณะดังนี้

  • ซึ่งช่วยให้คุณเรียกใช้ Intent ในเวลาและ/หรือช่วงเวลาที่กำหนด

  • คุณสามารถใช้ร่วมกับตัวรับการออกอากาศเพื่อกำหนดเวลางานหรือคำของานเพื่อดำเนินการอื่นๆ ได้

  • เหตุการณ์ทำงานนอกแอปพลิเคชัน คุณจึงใช้เหตุการณ์เพื่อทริกเกอร์เหตุการณ์หรือการดําเนินการได้แม้ว่าแอปจะไม่ทํางานอยู่และแม้ว่าอุปกรณ์จะอยู่ในโหมดสลีปก็ตาม

  • ซึ่งจะช่วยคุณลดข้อกำหนดด้านทรัพยากรของแอป คุณสามารถกำหนดเวลาการดำเนินการโดยไม่ต้องใช้ตัวจับเวลาหรือบริการที่ทำงานอย่างต่อเนื่อง

ตั้งปลุกแบบไม่แน่นอน

เมื่อแอปตั้งการปลุกที่ไม่ถูกต้อง ระบบจะส่งการปลุกในช่วงเวลาหนึ่งในอนาคต การปลุกที่ไม่แน่นอนจะรับประกันบางอย่างเกี่ยวกับเวลาในการส่งการปลุก ขณะเดียวกันก็เคารพข้อจำกัดในการประหยัดแบตเตอรี่ เช่น โหมดสลีป

นักพัฒนาแอปสามารถใช้การรับประกัน API ต่อไปนี้เพื่อปรับแต่งเวลาในการส่งการแจ้งเตือนที่ไม่ถูกต้อง

ส่งการปลุกหลังจากเวลาที่กำหนด

หากแอปเรียกใช้ set(), setInexactRepeating() หรือ setAndAllowWhileIdle() ระบบจะไม่ปลุกก่อนเวลาทริกเกอร์ที่ระบุ

ใน Android 12 (API ระดับ 31) ขึ้นไป ระบบจะเรียกใช้การปลุกภายใน 1 ชั่วโมงนับจากเวลาที่ทริกเกอร์ที่ระบุ เว้นแต่จะมีการใช้ข้อจำกัดการประหยัดแบตเตอรี่ เช่น โหมดประหยัดแบตเตอรี่หรือโหมดสลีป

ส่งการปลุกในช่วงเวลาที่กำหนด

หากแอปเรียกใช้ setWindow() การปลุกจะไม่ดังขึ้นก่อนเวลาทริกเกอร์ที่ระบุ ระบบจะส่งเสียงปลุกภายในกรอบเวลาที่กำหนดไว้โดยเริ่มตั้งแต่เวลาที่ทริกเกอร์ที่ระบุ เว้นแต่จะมีข้อจำกัดการประหยัดแบตเตอรี่

หากแอปกำหนดเป้าหมายเป็น Android 12 ขึ้นไป ระบบจะเลื่อนเวลาเรียกใช้การปลุกที่ไม่แน่นอนตามกรอบเวลาอย่างน้อย 10 นาที ด้วยเหตุนี้ ค่าพารามิเตอร์ windowLengthMillis ที่อยู่ต่ำกว่า 600000 จึงถูกตัดให้เหลือ 600000

ส่งการแจ้งเตือนแบบซ้ำในช่วงเวลาที่ค่อนข้างสม่ำเสมอ

หากแอปเรียกใช้ setInexactRepeating() ระบบจะเรียกใช้การแจ้งเตือนหลายรายการ ดังนี้

  1. การปลุกครั้งแรกจะดังขึ้นภายในกรอบเวลาที่กำหนด โดยเริ่มจากเวลาทริกเกอร์ที่ระบุ
  2. โดยปกติแล้วการปลุกครั้งถัดไปจะดังขึ้นหลังจากพ้นกรอบเวลาที่กำหนด ระยะเวลาระหว่างการเรียกใช้การปลุก 2 ครั้งติดต่อกันอาจแตกต่างกันไป

ตั้งปลุกในเวลาที่แน่นอน

ระบบจะเรียกการปลุกที่แน่นอนในเวลาที่แน่นอนในอนาคต

แอปส่วนใหญ่สามารถกำหนดเวลางานและกิจกรรมโดยใช้การปลุกที่ไม่แน่นอนเพื่อดำเนินการตามกรณีการใช้งานทั่วไปหลายรายการ หากฟังก์ชันหลักของแอปขึ้นอยู่กับการปลุกที่มีเวลาแน่นอน เช่น สําหรับแอปนาฬิกาปลุกหรือแอปปฏิทิน คุณก็ใช้การปลุกในเวลาที่แน่นอนแทนได้

กรณีการใช้งานที่อาจไม่จําเป็นต้องใช้การปลุกในเวลาที่แน่นอน

รายการต่อไปนี้แสดงเวิร์กโฟลว์ทั่วไปที่อาจไม่กำหนดให้ใช้การตั้งปลุกในเวลาที่แน่นอน

การกำหนดเวลาการดำเนินการตามช่วงเวลาตลอดอายุของแอป
คลาส Handler มีเมธอดที่มีประโยชน์หลายวิธีสำหรับจัดการการดำเนินการตามเวลา เช่น ทำงานบางอย่างทุกๆ n วินาทีขณะที่แอปทำงานอยู่ ดังนี้ postAtTime() และ postDelayed() โปรดทราบว่า API เหล่านี้ใช้เวลาทำงานของระบบ ไม่ใช่แบบเรียลไทม์
งานพื้นหลังที่กำหนดเวลาไว้ เช่น การอัปเดตแอปและการอัปโหลดบันทึก
WorkManager มีวิธีกำหนดเวลางานตามช่วงเวลาที่ต้องคำนึงถึงเวลา คุณสามารถระบุช่วงเวลาที่ทำงานซ้ำและ flexInterval (ขั้นต่ำ 15 นาที) เพื่อกำหนดรันไทม์แบบละเอียดสำหรับงาน
การดําเนินการที่ผู้ใช้ระบุซึ่งควรเกิดขึ้นหลังจากเวลาหนึ่งๆ (แม้ว่าระบบจะอยู่ในสถานะไม่มีการใช้งานก็ตาม)
ใช้การปลุกที่ไม่แน่นอน โปรดโทรไปที่ setAndAllowWhileIdle()
การดําเนินการที่ผู้ใช้ระบุซึ่งควรเกิดขึ้นหลังจากเวลาหนึ่งๆ
ใช้การปลุกที่ไม่แน่นอน โปรดโทรไปที่ set()
การดําเนินการที่ผู้ใช้ระบุซึ่งอาจเกิดขึ้นภายในกรอบเวลาที่ระบุ
ใช้การปลุกที่ไม่แน่นอน โปรดโทรไปที่ setWindow() โปรดทราบว่าหากแอปกำหนดเป้าหมายเป็น Android 12 ขึ้นไป ระยะเวลาของกรอบเวลาต่ำสุดที่อนุญาตคือ 10 นาที

วิธีตั้งปลุกในเวลาที่แน่นอน

แอปสามารถตั้งปลุกในเวลาที่แน่นอนได้โดยใช้วิธีใดวิธีหนึ่งต่อไปนี้ โดยวิธีการเหล่านี้จะเรียงลําดับเพื่อให้วิธีการที่อยู่ใกล้กับด้านล่างของรายการทํางานสําคัญกับเวลามากกว่า แต่ต้องใช้ทรัพยากรของระบบมากขึ้น

setExact()

เปิดการปลุกในเวลาที่เกือบจะแน่นอนในอนาคต ตราบใดที่มาตรการอื่นๆ ในการประหยัดแบตเตอรี่ไม่ได้มีผล

ใช้วิธีนี้เพื่อตั้งปลุกในเวลาที่แน่นอน เว้นแต่งานของแอปจะมีความเกี่ยวข้องกับเวลาของผู้ใช้

setExactAndAllowWhileIdle()

เปิดการปลุกในเวลาที่เกือบจะแน่นอนในอนาคต แม้ว่าจะมีมาตรการประหยัดแบตเตอรี่อยู่ก็ตาม

setAlarmClock()

เปิดการปลุกในเวลาที่แน่นอนในอนาคต เนื่องจากผู้ใช้จะเห็นการแจ้งเตือนเหล่านี้อย่างชัดเจน ระบบจึงไม่มีการปรับเวลานำส่ง ระบบจะระบุว่าการแจ้งเตือนเหล่านี้สำคัญที่สุดและจะไม่ออกจากโหมดพลังงานต่ำหากจำเป็นในการส่งการแจ้งเตือน

การใช้ทรัพยากรของระบบ

เมื่อระบบทริกเกอร์การปลุกที่ตรงกับการตั้งค่าของแอป อุปกรณ์จะใช้ทรัพยากรจำนวนมาก เช่น อายุการใช้งานแบตเตอรี่ โดยเฉพาะหากอยู่ในโหมดประหยัดพลังงาน นอกจากนี้ ระบบยังจัดกลุ่มคำขอเหล่านี้เพื่อใช้ทรัพยากรอย่างมีประสิทธิภาพมากขึ้นได้ยาก

เราขอแนะนําอย่างยิ่งให้คุณสร้างการแจ้งเตือนที่ไม่แน่นอนทุกครั้งที่เป็นไปได้ หากต้องการทำงานเป็นเวลานานขึ้น ให้กำหนดเวลาโดยใช้ WorkManager หรือ JobScheduler จาก BroadcastReceiver ของนาฬิกาปลุก หากต้องการทำงานขณะที่อุปกรณ์อยู่ในโหมดสลีป ให้สร้างการปลุกในเวลาที่ไม่แน่นอนโดยใช้ setAndAllowWhileIdle() แล้วเริ่มงานจากการปลุก

ประกาศสิทธิ์การปลุกในเวลาที่แน่นอนที่เหมาะสม

หากแอปกำหนดเป้าหมายเป็น Android 12 ขึ้นไป คุณต้องขอสิทธิ์เข้าถึงพิเศษของแอปสำหรับ "การปลุกและการช่วยเตือน" โดยประกาศสิทธิ์ SCHEDULE_EXACT_ALARM ในไฟล์ Manifest ของแอป ดังที่แสดงในข้อมูลโค้ดต่อไปนี้

<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 เช่น กับ setExact API ก็ไม่จำเป็นต้องมีสิทธิ์ SCHEDULE_EXACT_ALARM

การใช้สิทธิ์ SCHEDULE_EXACT_ALARM

สิทธิ์ SCHEDULE_EXACT_ALARM ต้องได้รับอนุญาตจากผู้ใช้ ซึ่งแตกต่างจาก USE_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. ใน UI ของแอป ให้อธิบายให้ผู้ใช้ทราบว่าเหตุใดแอปจึงต้องตั้งเวลาการปลุกให้ตรงเวลา
  2. เรียกใช้ Intent ที่มีการดำเนินการของ Intent ACTION_REQUEST_SCHEDULE_EXACT_ALARM

ตั้งปลุกซ้ำ

การปลุกซ้ำช่วยให้ระบบแจ้งเตือนแอปของคุณตามกำหนดการที่เกิดซ้ำได้

การปลุกที่ออกแบบมาไม่ดีอาจทำให้แบตเตอรี่หมดและทำให้เซิร์ฟเวอร์มีภาระงานมาก ด้วยเหตุนี้ ใน Android 4.4 (API ระดับ 19) ขึ้นไป การปลุกซ้ำทั้งหมดจึงเป็นการปลุกที่ไม่ถูกต้อง

การปลุกซ้ำมีลักษณะดังนี้

  • ประเภทการปลุก ดูการพูดคุยเพิ่มเติมที่หัวข้อเลือกประเภทการปลุก

  • เวลาทริกเกอร์ หากเวลาทริกเกอร์ที่คุณระบุเป็นอดีต ระบบจะทริกเกอร์การปลุกทันที

  • ช่วงเวลาการปลุก เช่น วันละครั้ง ทุกชั่วโมง หรือทุก 5 นาที

  • Intent ที่รอดำเนินการซึ่งจะเริ่มทำงานเมื่อระบบทริกเกอร์การปลุก เมื่อคุณตั้งปลุกครั้งที่ 2 ที่ใช้ Intent ที่รอดำเนินการเดียวกัน ปลุกครั้งที่ 2 จะแทนที่การปลุกเดิม

หากต้องการยกเลิก 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);
}

เลือกประเภทการปลุก

ข้อควรพิจารณาประการแรกในการใช้การปลุกซ้ำคือประเภทการปลุก

นาฬิกาทั่วไปสำหรับการตั้งปลุกมี 2 ประเภท ได้แก่ "เวลาจริงที่ผ่านไป" และ "นาฬิกาแบบเรียลไทม์" (RTC) เวลาจริงที่ผ่านไปจะใช้ "เวลานับตั้งแต่ระบบบูต" เป็นข้อมูลอ้างอิง และนาฬิกาแบบเรียลไทม์จะใช้เวลา UTC (นาฬิกาตั้งโต๊ะ) ซึ่งหมายความว่าเวลาจริงที่ผ่านไปเหมาะสำหรับการตั้งปลุกตามช่วงเวลา (เช่น ปลุกที่ทำงานทุก 30 วินาที) เนื่องจากไม่ได้รับผลกระทบจากเขตเวลาหรือภาษา นาฬิกาแบบเรียลไทม์เหมาะสําหรับการปลุกที่ขึ้นอยู่กับภาษาปัจจุบันมากกว่า

ทั้ง 2 ประเภทมีเวอร์ชัน "การปลุก" ซึ่งจะสั่งให้ 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);

วิธีปลุกอุปกรณ์ให้ปลุกแบบครั้งเดียว (ไม่ซ้ำ) ใน 1 นาที

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 น. และ repetir 1 ครั้งต่อวันในเวลาเดียวกัน

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 ในไฟล์ Manifest ของแอปพลิเคชัน ซึ่งจะช่วยให้แอปของคุณได้รับ ACTION_BOOT_COMPLETED ที่ระบบประกาศหลังจากบูตเสร็จ (การดำเนินการนี้จะได้ผลก็ต่อเมื่อผู้ใช้เปิดแอปอย่างน้อย 1 ครั้งแล้วเท่านั้น)

    <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. เพิ่มตัวรับลงในไฟล์ Manifest ของแอปด้วยตัวกรอง 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>

    โปรดทราบว่าในไฟล์ Manifest มีการตั้งค่าตัวรับการบูตเป็น 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);

    เมื่อเปิดใช้รีซีฟเวอร์ด้วยวิธีนี้ รีซีฟเวอร์จะเปิดใช้อยู่แม้ว่าผู้ใช้จะรีบูตอุปกรณ์ก็ตาม กล่าวคือ การเปิดใช้ตัวรับแบบเป็นโปรแกรมจะลบล้างการตั้งค่าไฟล์ Manifest แม้ว่าจะมีการรีบูตก็ตาม ตัวรับจะเปิดอยู่จนกว่าแอปจะปิดใช้ คุณปิดใช้รีซีฟเวอร์ได้ (เช่น หากผู้ใช้ยกเลิกการปลุก) โดยทำดังนี้

    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 (API ระดับ 23) รองรับโหมดDoze ซึ่งช่วยยืดอายุการใช้งานแบตเตอรี่ของอุปกรณ์ การปลุกจะไม่ทำงานเมื่ออุปกรณ์อยู่ในโหมดสลีป ระบบจะเลื่อนการปลุกที่ตั้งเวลาไว้จนกว่าอุปกรณ์จะออกจากโหมดสลีป หากต้องการทำงานให้เสร็จสมบูรณ์แม้ในขณะที่อุปกรณ์ไม่ได้ใช้งาน คุณมีตัวเลือกหลายอย่างดังนี้

  • ตั้งการปลุกที่แน่นอน

  • ใช้ WorkManager API ซึ่งสร้างขึ้นเพื่อทำงานเบื้องหลัง คุณสามารถระบุว่าระบบควรเร่งงานของคุณเพื่อให้งานเสร็จโดยเร็วที่สุด ดูข้อมูลเพิ่มเติมได้ที่หัวข้อตั้งเวลางานด้วย WorkManager

แนวทางปฏิบัติแนะนำ

ตัวเลือกที่คุณเลือกในการออกแบบการปลุกซ้ำอาจมีผลต่อวิธีที่แอปใช้ (หรือละเมิด) ทรัพยากรระบบ ตัวอย่างเช่น สมมติว่าแอปยอดนิยมที่ซิงค์กับเซิร์ฟเวอร์ หากการดำเนินการซิงค์อิงตามเวลาของนาฬิกาและอินสแตนซ์ทั้งหมดของแอปซิงค์กันในเวลา 23:00 น. ภาระงานในเซิร์ฟเวอร์อาจส่งผลให้เกิดความล่าช้าสูงหรือแม้แต่ "การปฏิเสธการให้บริการ" ทำตามแนวทางปฏิบัติแนะนำต่อไปนี้ในการใช้การปลุก

  • เพิ่มการสุ่ม (Jitter) ให้กับคำขอเครือข่ายที่ทริกเกอร์ขึ้นเนื่องจากการปลุกซ้ำ

    • ทำงานใดๆ ก็ตามในพื้นที่เมื่อระบบเปิดการแจ้งเตือน "การทำงานในเครื่อง" หมายถึงการดำเนินการใดๆ ที่ไม่ได้เข้าถึงเซิร์ฟเวอร์หรือต้องใช้ข้อมูลจากเซิร์ฟเวอร์

    • ในขณะเดียวกัน ให้ตั้งเวลาการปลุกที่มีคำขอเครือข่ายให้ทำงานเป็นช่วงๆ แบบสุ่ม

  • ตั้งความถี่การปลุกให้น้อยที่สุด

  • อย่าปลุกอุปกรณ์โดยไม่จำเป็น (ลักษณะการทำงานนี้ขึ้นอยู่กับประเภทการปลุกตามที่อธิบายไว้ในเลือกประเภทการปลุก)

  • อย่าตั้งค่าเวลาทริกเกอร์ของนาฬิกาปลุกให้แม่นยำเกินความจำเป็น

    ใช้ setInexactRepeating() instead of setRepeating() เมื่อคุณใช้ setInexactRepeating() ระบบ Android จะซิงค์การปลุกซ้ำจากหลายแอปและเปิดการปลุกพร้อมกัน ซึ่งจะช่วยลดจำนวนครั้งที่ระบบต้องปลุกอุปกรณ์ จึงช่วยประหยัดแบตเตอรี่ ตั้งแต่ Android 4.4 (API ระดับ 19) เป็นต้นไป การปลุกซ้ำทั้งหมดจะเป็นการปลุกที่ไม่แน่นอน โปรดทราบว่าแม้ว่า setInexactRepeating() จะมีประสิทธิภาพดีกว่า setRepeating() แต่ก็ยังอาจทำให้เซิร์ฟเวอร์ทำงานหนักเกินไปได้หากอินสแตนซ์ทั้งหมดของแอปเข้าถึงเซิร์ฟเวอร์ในเวลาใกล้เคียงกัน ดังนั้นสําหรับคําขอเครือข่าย ให้เพิ่มความสุ่มให้กับการแจ้งเตือนดังที่ได้กล่าวไว้ก่อนหน้านี้

  • หลีกเลี่ยงการตั้งปลุกตามเวลาของนาฬิกา หากเป็นไปได้

    การปลุกซ้ำที่อิงตามเวลาทริกเกอร์ที่แน่นอนจะปรับขนาดได้ไม่ดี ใช้ ELAPSED_REALTIME หากทำได้ ประเภทต่างๆ ของการปลุกจะอธิบายไว้อย่างละเอียดในส่วนต่อไปนี้