Lên lịch cho chuông báo

Chuông báo (dựa trên AlarmManager cho phép bạn thực hiện các hoạt động dựa trên thời gian bên ngoài vòng đời của ứng dụng. Ví dụ: bạn có thể sử dụng chuông báo để bắt đầu một hoạt động diễn ra trong thời gian dài, chẳng hạn như bắt đầu một dịch vụ mỗi ngày một lần để tải thông tin dự báo thời tiết xuống.

Báo thức có các đặc điểm sau:

  • Chúng cho phép bạn kích hoạt Ý định vào những thời điểm và/hoặc khoảng thời gian đã đặt.

  • Bạn có thể dùng các broadcast receiver này cùng với broadcast receiver để lên lịch công việc hoặc WorkRequest để thực hiện các công việc khác các toán tử.

  • Chúng hoạt động bên ngoài ứng dụng của bạn, vì vậy, bạn có thể sử dụng chúng để kích hoạt các sự kiện hoặc hành động ngay cả khi ứng dụng của bạn không chạy và ngay cả khi thiết bị chính nó đang ngủ.

  • Các thành phần này giúp bạn giảm thiểu các yêu cầu về tài nguyên của ứng dụng. Bạn có thể lên lịch hoạt động mà không cần dựa vào đồng hồ hẹn giờ hay liên tục chạy dịch vụ.

Đặt chuông báo không chính xác

Khi một ứng dụng đặt chuông báo không chính xác, hệ thống sẽ đưa ra chuông báo vào thời điểm nào đó trong tương lai. Chuông báo không chính xác đưa ra một số đảm bảo về thời gian của gửi cảnh báo trong khi vẫn tuân thủ các hạn chế tiết kiệm pin như Nghỉ ngơi.

Nhà phát triển có thể tận dụng các đảm bảo API sau đây để tuỳ chỉnh thời gian gửi chuông báo không chính xác.

Gửi chuông báo sau một thời điểm cụ thể

Nếu ứng dụng của bạn gọi set(), setInexactRepeating(), hoặc setAndAllowWhileIdle(), chuông báo không bao giờ tắt trước thời gian kích hoạt được cung cấp.

Trên Android 12 (API cấp 31) trở lên, hệ thống sẽ gọi chuông báo trong một lần giờ tính từ thời gian kích hoạt được cung cấp, trừ phi có bất kỳ hạn chế nào về tiết kiệm pin có hiệu lực như trình tiết kiệm pin hoặc Nghỉ ngơi.

Đưa ra chuông báo trong một khoảng thời gian

Nếu ứng dụng của bạn gọi setWindow(), thì chuông báo sẽ không bao giờ tắt trước khi chuông báo được cung cấp thời gian kích hoạt. Chuông báo sẽ không có hiệu lực trừ phi có biện pháp hạn chế nào giúp tiết kiệm pin được phân phối trong khoảng thời gian chỉ định, bắt đầu từ điều kiện kích hoạt nhất định bất cứ lúc nào.

Nếu ứng dụng của bạn nhắm đến Android 12 trở lên, thì hệ thống có thể trì hoãn yêu cầu báo thức không chính xác theo khung thời gian ít nhất 10 phút. Cho lý do này, các giá trị tham số windowLengthMillis trong 600000 được cắt thành 600000

Báo thức lặp lại gần như đều đặn

Nếu ứng dụng của bạn gọi setInexactRepeating(), hệ thống gọi nhiều chuông báo:

  1. Chuông báo đầu tiên sẽ báo trong khoảng thời gian được chỉ định, bắt đầu từ thời gian kích hoạt nhất định.
  2. Các chuông báo tiếp theo thường đổ chuông sau khoảng thời gian đã chỉ định trôi qua. Thời gian giữa hai lần gọi báo thức liên tiếp có thể khác nhau.

Đặt chuông báo chính xác

Hệ thống sẽ gọi thông báo chính xác tại một thời điểm chính xác trong tương lai.

Hầu hết các ứng dụng đều có thể lên lịch cho các tác vụ và sự kiện bằng cách sử dụng thông báo không chính xác để hoàn thành một số trường hợp sử dụng phổ biến. Nếu tính năng cốt lõi của ứng dụng chức năng này phụ thuộc vào một chuông báo được xác định thời gian chính xác, chẳng hạn như đối với ứng dụng đồng hồ báo thức hoặc ứng dụng lịch—thì có thể sử dụng báo thức chính xác thay thế.

Các trường hợp sử dụng có thể không cần đến chuông báo chính xác

Danh sách sau đây cho thấy những quy trình công việc thường gặp có thể không cần đến chuông báo chính xác:

Lên lịch cho các hoạt động định thời gian trong suốt vòng đời của ứng dụng
Lớp Handler bao gồm một số chính sách để xử lý các hoạt động về thời gian, chẳng hạn như thực hiện một số công việc mỗi n giây khi ứng dụng của bạn đang hoạt động: postAtTime()postDelayed(). Lưu ý rằng các API này phụ thuộc vào thời gian hoạt động của hệ thống chứ không phải theo thời gian thực.
Thao tác ở chế độ nền đã lên lịch, chẳng hạn như cập nhật ứng dụng và tải nhật ký lên
WorkManager cung cấp cách lập lịch định kỳ có giới hạn thời gian cơ quan. Bạn có thể đặt một khoảng thời gian lặp lại và flexInterval (tối thiểu 15 phút) để xác định thời gian chạy chi tiết cho công việc.
Hành động do người dùng chỉ định sẽ xảy ra sau một thời gian cụ thể (ngay cả khi hệ thống ở trạng thái rảnh)
Sử dụng chuông báo không chính xác. Cụ thể, hãy gọi setAndAllowWhileIdle().
Hành động do người dùng chỉ định sẽ xảy ra sau một thời gian cụ thể
Sử dụng chuông báo không chính xác. Cụ thể, hãy gọi set().
Hành động do người dùng chỉ định có thể xảy ra trong một khoảng thời gian chỉ định
Sử dụng chuông báo không chính xác. Cụ thể, hãy gọi setWindow(). Xin lưu ý rằng nếu ứng dụng của bạn nhắm đến Android 12 trở lên, thì thời lượng cho phép là 10 phút.

Cách đặt chuông báo chính xác

Ứng dụng của bạn có thể đặt chuông báo chính xác bằng một trong các phương thức sau. Các phương thức này được sắp xếp sao cho các quảng cáo ở gần cuối danh sách phân phát nhiều hơn các nhiệm vụ quan trọng về thời gian nhưng đòi hỏi nhiều tài nguyên hệ thống hơn.

setExact()

Gọi chuông báo vào một thời điểm gần như chính xác trong tương lai, miễn là các biện pháp tiết kiệm pin không có hiệu quả.

Hãy sử dụng phương thức này để đặt chuông báo chính xác, trừ trường hợp ứng dụng của bạn hoạt động vô cùng quan trọng đối với người dùng.

setExactAndAllowWhileIdle()

Gọi chuông báo vào thời điểm gần như chính xác trong tương lai, ngay cả khi tiết kiệm pin đang áp dụng.

setAlarmClock()

Gọi chuông báo vào một thời điểm chính xác trong tương lai. Vì các chuông báo này dễ thấy đối với người dùng, nhưng hệ thống không bao giờ điều chỉnh thời gian phân phối. Chiến lược phát hành đĩa đơn hệ thống xác định những báo thức này là những báo thức quan trọng nhất và để tiết kiệm pin các chế độ khác nhau nếu cần để gửi báo thức.

Mức tiêu thụ tài nguyên hệ thống

Khi hệ thống kích hoạt chuông báo chính xác mà ứng dụng của bạn đặt, thiết bị tiêu tốn rất nhiều tài nguyên, chẳng hạn như thời lượng pin, đặc biệt nếu ở chế độ tiết kiệm điện. Hơn nữa, hệ thống không thể dễ dàng phân lô các yêu cầu này nhằm sử dụng tài nguyên hiệu quả hơn.

Bạn nên tạo thông báo không chính xác bất cứ khi nào nhất có thể. Để làm việc lâu hơn, hãy lên lịch bằng cách sử dụng WorkManager hoặc JobScheduler từ chuông báo của bạn BroadcastReceiver. Để thực hiện công việc trong khi thiết bị đang ở chế độ Nghỉ, hãy tạo chuông báo không chính xác bằng cách sử dụng setAndAllowWhileIdle()! và bắt đầu công việc từ chuông báo.

Khai báo quyền thích hợp về chuông báo chính xác

Nếu ứng dụng của bạn nhắm đến Android 12 trở lên, bạn phải có được "Chuông báo và lời nhắc" quyền truy cập đặc biệt vào ứng dụng. Để thực hiện việc này, hãy khai báo SCHEDULE_EXACT_ALARM quyền trong tệp kê khai của ứng dụng, như minh hoạ trong đoạn mã sau:

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

Nếu ứng dụng của bạn nhắm đến Android 13 (API cấp 33) trở lên, thì bạn có thể chọn khai báo thuộc tính SCHEDULE_EXACT_ALARM hoặc USE_EXACT_ALARM quyền.

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

Mặc dù cả quyền SCHEDULE_EXACT_ALARMUSE_EXACT_ALARM cho biết các khả năng giống nhau, chúng được cấp theo cách khác nhau và hỗ trợ các tên khác nhau các trường hợp sử dụng. Ứng dụng của bạn nên sử dụng chuông báo chính xác và khai báo một trong hai Quyền SCHEDULE_EXACT_ALARM hoặc USE_EXACT_ALARM, chỉ khi người dùng trong ứng dụng của bạn đòi hỏi các hành động được xác định thời gian chính xác.

USE_EXACT_ALARM

SCHEDULE_EXACT_ALARM

  • Do người dùng cấp
  • Nhiều trường hợp sử dụng hơn
  • Ứng dụng phải xác nhận rằng quyền này chưa bị thu hồi

Quyền SCHEDULE_EXACT_ALARM không được cấp trước cho các lượt cài đặt mới ứng dụng nhắm đến Android 13 (API cấp 33) trở lên. Nếu người dùng chuyển ứng dụng vào một thiết bị chạy Android 14 thông qua tác vụ sao lưu và khôi phục, Quyền SCHEDULE_EXACT_ALARM sẽ bị từ chối trên thiết bị mới. Tuy nhiên, nếu một ứng dụng hiện có đã có quyền này, ứng dụng này sẽ được cấp trước khi nâng cấp thiết bị lên Android 14.

Lưu ý: Nếu bạn đặt chuông báo chính xác bằng OnAlarmListener chẳng hạn như với setExact API, quyền SCHEDULE_EXACT_ALARM là không bắt buộc.

Sử dụng quyền SCHEDULE_EXACT_ALARM

Không giống như USE_EXACT_ALARM, quyền SCHEDULE_EXACT_ALARM phải do người dùng cấp. Cả người dùng và hệ thống đều có thể thu hồi Quyền SCHEDULE_EXACT_ALARM.

Để kiểm tra xem quyền đã được cấp cho ứng dụng của bạn hay chưa, hãy gọi canScheduleExactAlarms() trước khi cố gắng đặt một chuông báo chính xác. Khi quyền SCHEDULE_EXACT_ALARM bị thu hồi đối với ứng dụng của bạn, ứng dụng ngừng hoạt động và tất cả chuông báo chính xác trong tương lai bị huỷ. Điều này cũng có nghĩa là giá trị được canScheduleExactAlarms() luôn có hiệu lực trong toàn bộ vòng đời của ứng dụng.

Khi được cấp quyền SCHEDULE_EXACT_ALARMS cho ứng dụng của bạn, thì hệ thống gửi cho nó ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED truyền tin. Ứng dụng của bạn nên triển khai thông báo truyền tin receiver này thực hiện sau:

  1. Xác nhận rằng ứng dụng của bạn vẫn có quyền truy cập đặc biệt. Để thực hiện việc này, hãy gọi canScheduleExactAlarms(). Bước kiểm tra này bảo vệ ứng dụng của bạn khỏi trường hợp người dùng cấp quyền quyền rồi thu hồi quyền đó gần như ngay lập tức.
  2. Lên lịch lại mọi chuông báo chính xác mà ứng dụng của bạn cần, dựa trên trạng thái hiện tại của chuông báo. Logic này sẽ tương tự như những gì ứng dụng của bạn sẽ thực hiện khi nhận được ACTION_BOOT_COMPLETED truyền tin.

Yêu cầu người dùng cấp quyền cho SCHEDULE_EXACT_ALARM

Tuỳ chọn này có tên là &quot;Cho phép đặt chuông báo và lời nhắc&quot;
Hình 1. "Chuông báo và lời nhắc" quyền truy cập đặc biệt Trong phần cài đặt hệ thống, nơi người dùng có thể cho phép ứng dụng của bạn đặt chuông báo.

Nếu cần, bạn có thể đưa người dùng đến phần Chuông báo và màn hình lời nhắc trong hệ thống như được minh hoạ trong Hình 1. Để thực hiện điều này, vui lòng hoàn thành các bước sau:

  1. Trong giao diện người dùng của ứng dụng, hãy giải thích cho người dùng lý do ứng dụng cần lên lịch chính xác các chuông báo.
  2. Gọi một ý định bao gồm ACTION_REQUEST_SCHEDULE_EXACT_ALARM thao tác theo ý định.

Đặt báo thức lặp lại

Việc lặp lại chuông báo cho phép hệ thống thông báo cho ứng dụng của bạn .

Chuông báo có thiết kế không phù hợp có thể làm tiêu hao pin và làm tăng tải đáng kể máy chủ. Vì lý do này, trên Android 4.4 (API cấp 19) trở lên, tất cả báo thức lặp lại là chuông báo không chính xác.

Chuông báo lặp lại có các đặc điểm sau:

  • Một loại chuông báo. Để thảo luận thêm, hãy xem phần Chọn loại chuông báo.

  • Thời gian kích hoạt. Nếu thời gian kích hoạt bạn chỉ định là trong quá khứ, báo thức kích hoạt ngay lập tức.

  • Khoảng thời gian báo thức. Ví dụ: 1 lần/ngày, mỗi giờ hoặc 5 phút một lần.

  • Một ý định đang chờ xử lý kích hoạt khi chuông báo được kích hoạt. Khi bạn đặt một chuông báo thứ hai sử dụng cùng một ý định đang chờ xử lý sẽ thay thế chuông báo ban đầu.

Để huỷ PendingIntent(), hãy dùng FLAG_NO_CREATE đến PendingIntent.getService() để lấy một thực thể của ý định (nếu có), rồi chuyển ý định đó đến 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);
}

Chọn một loại chuông báo

Một trong những yếu tố đầu tiên cần cân nhắc khi sử dụng chuông báo lặp lại là loại chuông báo phải có.

Có 2 loại đồng hồ chung cho chuông báo: "đã trôi qua theo thời gian thực" và "đồng hồ thời gian thực" (RTC). Thời gian thực đã trôi qua sử dụng "thời gian kể từ khi khởi động hệ thống" với tư cách là tham chiếu và đồng hồ thời gian thực sử dụng thời gian UTC (đồng hồ tường). Điều này có nghĩa là thời gian thực đã trôi qua thì phù hợp với việc đặt báo thức dựa trên khoảng thời gian (ví dụ: ví dụ: báo thức kích hoạt 30 giây một lần) vì không bị ảnh hưởng bởi múi giờ hoặc ngôn ngữ. Loại đồng hồ thời gian thực phù hợp hơn với các chuông báo phụ thuộc vào ngôn ngữ hiện tại.

Cả hai loại đều có trạng thái "đánh thức" phiên bản này sẽ đánh thức CPU của thiết bị nếu màn hình tắt. Điều này đảm bảo chuông báo sẽ kích hoạt vào thời gian đã lên lịch. Điều này rất hữu ích nếu ứng dụng có phần phụ thuộc về thời gian. Ví dụ: nếu có một cửa sổ giới hạn để thực hiện thao tác cụ thể. Nếu bạn không sử dụng phiên bản đánh thức của loại báo thức bạn chọn, thì tất cả báo thức lặp lại sẽ kích hoạt khi thiết bị của bạn bật vào lần tiếp theo.

Nếu bạn chỉ cần chuông báo kích hoạt vào một khoảng thời gian cụ thể (ví dụ: mỗi nửa giờ), hãy sử dụng một trong các loại thời gian thực đã trôi qua. Nhìn chung, đây là là lựa chọn tốt hơn.

Nếu bạn cần chuông báo kích hoạt vào một thời điểm cụ thể trong ngày, hãy chọn một chuông báo của các loại đồng hồ thời gian thực dựa trên đồng hồ. Tuy nhiên, cần lưu ý rằng phương pháp này có thể có một số hạn chế. Có thể ứng dụng không dịch tốt sang các ngôn ngữ khác và nếu người dùng thay đổi chế độ cài đặt giờ của thiết bị, nên có thể gây ra hành vi không mong muốn trong ứng dụng của bạn. Việc sử dụng loại chuông báo theo thời gian thực cũng không điều chỉnh được theo tỷ lệ, vì thảo luận ở trên. Chúng tôi khuyên bạn nên sử dụng "thời gian thực đã trôi qua" chuông báo nếu có thể.

Dưới đây là danh sách các loại:

  • ELAPSED_REALTIME: Kích hoạt ý định đang chờ xử lý dựa trên khoảng thời gian kể từ khi thiết bị đã khởi động, nhưng không đánh thức thiết bị. Chiến lược phát hành đĩa đơn thời gian đã trôi qua bao gồm bất kỳ thời điểm nào thiết bị ở trạng thái ngủ.

  • ELAPSED_REALTIME_WAKEUP: Đánh thức thiết bị và kích hoạt ý định đang chờ xử lý sau thời lượng đã chỉ định đã trôi qua kể từ khi khởi động thiết bị.

  • RTC: Kích hoạt ý định đang chờ xử lý tại thời điểm được chỉ định nhưng không đánh thức thiết bị.

  • RTC_WAKEUP: Thức dậy thiết bị để kích hoạt ý định đang chờ xử lý tại thời điểm được chỉ định.

Ví dụ về chuông báo theo thời gian thực đã trôi qua

Dưới đây là một số ví dụ về cách sử dụng ELAPSED_REALTIME_WAKEUP

Đánh thức thiết bị để kích hoạt chuông báo sau 30 phút và cứ 30 phút một lần sau đó:

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);

Đánh thức thiết bị để kích hoạt chuông báo một lần (không lặp lại) sau một phút:

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);

Ví dụ về chuông báo theo thời gian thực

Sau đây là một số ví dụ về cách sử dụng RTC_WAKEUP.

Đánh thức thiết bị để kích hoạt chuông báo vào khoảng 2 giờ chiều và lặp lại một lần mỗi ngày cùng lúc:

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);

Đánh thức thiết bị để kích hoạt báo thức vào đúng 8:30 sáng và cứ 20 phút một lần sau đó:

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);

Quyết định độ chính xác của chuông báo

Như đã mô tả trước đó, việc chọn loại chuông báo thường là bước đầu tiên trong đang tạo một chuông báo. Một điểm khác biệt nữa là bạn cần chuông báo chính xác đến mức nào sẽ là ai. Đối với hầu hết các ứng dụng, setInexactRepeating() là lựa chọn đúng đắn. Khi bạn sử dụng phương thức này, Android sẽ đồng bộ hoá nhiều chuông báo lặp lại không chính xác và chuông báo kích hoạt chúng cùng một lúc. Điều này sẽ làm giảm mức tiêu hao pin.

Tránh sử dụng chuông báo chính xác nếu có thể. Tuy nhiên, đối với ứng dụng hiếm có cài đặt theo yêu cầu về thời gian, bạn có thể đặt thông báo chính xác bằng cách gọi setRepeating().

Với setInexactRepeating(), bạn không thể chỉ định khoảng thời gian tuỳ chỉnh như cách bạn có thể setRepeating(). Bạn phải sử dụng một trong các hằng số khoảng thời gian, chẳng hạn như INTERVAL_FIFTEEN_MINUTES! INTERVAL_DAY, và cứ tiếp tục như vậy. Xem AlarmManager để xem danh sách đầy đủ.

Hủy chuông báo

Tuỳ thuộc vào ứng dụng của bạn, bạn có thể muốn thêm khả năng huỷ chuông báo. Để huỷ chuông báo, hãy gọi cancel() trên Alarm Manager, truyền PendingIntent bạn không muốn dùng nữa để kích hoạt. Ví dụ:

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);
}

Bắt đầu chuông báo khi thiết bị khởi động lại

Theo mặc định, tất cả chuông báo đều bị huỷ khi thiết bị tắt. Để ngăn điều này xảy ra, bạn có thể thiết kế ứng dụng của mình để tự động khởi động lại báo thức lặp lại nếu người dùng khởi động lại thiết bị. Chiến dịch này đảm bảo rằng AlarmManager sẽ tiếp tục thực hiện tác vụ của mình mà người dùng không cần khởi động lại chuông báo theo cách thủ công.

Sau đây là các bước:

  1. Đặt RECEIVE_BOOT_COMPLETED quyền trong tệp kê khai của ứng dụng. Điều này cho phép ứng dụng của bạn nhận được ACTION_BOOT_COMPLETED được phát sau khi hệ thống khởi động xong (điều này chỉ hiệu quả nếu ứng dụng đã được người dùng khởi chạy ít nhất một lần):

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
  2. Triển khai một BroadcastReceiver để nhận thông báo:

    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. Thêm trình nhận vào tệp kê khai của ứng dụng bằng bộ lọc ý định bộ lọc trên ACTION_BOOT_COMPLETED hành động:

    <receiver android:name=".SampleBootReceiver"
            android:enabled="false">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"></action>
        </intent-filter>
    </receiver>

    Lưu ý rằng trong tệp kê khai, trình nhận khởi động được đặt thành android:enabled="false". Điều này có nghĩa là người nhận sẽ sẽ không được gọi trừ phi ứng dụng bật lệnh đó một cách rõ ràng. Điều này ngăn chặn khởi động receiver một cách không cần thiết. Bạn có thể bật một receiver (ví dụ: nếu người dùng đặt chuông báo) như sau:

    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);
    

    Sau khi bạn bật trình nhận theo cách này, trình nhận sẽ luôn bật, ngay cả khi người dùng khởi động lại thiết bị. Nói cách khác, việc bật trình nhận theo cách có lập trình ghi đè chế độ cài đặt tệp kê khai, ngay cả khi khởi động lại. Người nhận sẽ ở lại bật cho đến khi ứng dụng của bạn tắt tính năng đó. Bạn có thể tắt một receiver (ví dụ: nếu người dùng huỷ chuông báo) như sau:

    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);
    

Gọi chuông báo khi thiết bị ở chế độ Nghỉ

Thiết bị chạy hỗ trợ Android 6.0 (API cấp 23) Nghỉ ngơi của bạn giúp kéo dài thời lượng pin của thiết bị. Chuông báo không kích hoạt khi thiết bị đang ở trong Chế độ nghỉ Mọi chuông báo đã lên lịch đều bị trì hoãn cho đến khi thiết bị thoát khỏi chế độ Nghỉ. Nếu bạn cần hoàn tất công việc ngay cả khi thiết bị ở trạng thái rảnh, có một số tuỳ chọn hiện có:

  • Đặt thông báo chính xác.

  • Sử dụng API WorkManager được xây dựng để hoạt động hiệu quả công việc trong nền. Bạn có thể cho biết rằng hệ thống cần đẩy nhanh công việc của bạn để để công việc hoàn tất sớm nhất có thể. Để biết thêm thông tin, hãy xem Lên lịch các tác vụ bằng WorkManager

Các phương pháp hay nhất

Mọi lựa chọn bạn đưa ra khi thiết kế chuông báo lặp lại đều có thể dẫn đến hậu quả về cách ứng dụng của bạn sử dụng (hoặc lạm dụng) tài nguyên hệ thống. Ví dụ: hãy tưởng tượng một một ứng dụng phổ biến đồng bộ hoá với một máy chủ. Nếu hoạt động đồng bộ hoá dựa trên đồng hồ và mọi phiên bản của ứng dụng sẽ đồng bộ hoá lúc 11 giờ đêm, tải trên thì có thể khiến độ trễ cao hoặc thậm chí "từ chối dịch vụ". Hãy làm theo các phương pháp hay nhất sau đây khi sử dụng chuông báo:

  • Thêm độ ngẫu nhiên (biến động) vào bất kỳ yêu cầu mạng nào kích hoạt do một chuông báo lặp lại:

    • Thực hiện mọi thao tác cục bộ khi chuông báo kích hoạt. "Nơi làm việc tại địa phương" có nghĩa là bất cứ điều gì không truy cập vào máy chủ hoặc yêu cầu dữ liệu từ máy chủ.

    • Đồng thời, hãy lên lịch cho chuông báo chứa yêu cầu mạng để kích hoạt trong khoảng thời gian ngẫu nhiên nào đó.

  • Duy trì tần suất báo thức ở mức tối thiểu.

  • Không đánh thức thiết bị khi không cần thiết (hành vi này được xác định bởi loại chuông báo, như mô tả trong Chọn loại chuông báo).

  • Đừng đặt thời gian kích hoạt của báo thức chính xác hơn mức cần thiết.

    Sử dụng setInexactRepeating() thay vì setRepeating(). Khi bạn sử dụng setInexactRepeating(), Android đồng bộ hoá chuông báo lặp lại từ nhiều ứng dụng và lệnh kích hoạt chúng cùng một lúc. Điều này giúp giảm tổng số lần hệ thống phải đánh thức do đó làm giảm mức tiêu hao pin. Kể từ Android 4.4 (API cấp 19), tất cả chuông báo lặp lại đều là chuông báo không chính xác. Ghi chú trong khi setInexactRepeating() là một sự cải thiện so với setRepeating(), nó vẫn có thể làm máy chủ bị choáng ngợp nếu mỗi phiên bản của ứng dụng đều truy cập vào máy chủ cùng một khoảng thời gian. Do đó, đối với các yêu cầu về mạng, hãy thêm ngẫu nhiên vào các chuông báo của bạn, như đã thảo luận trước đó.

  • Tránh đặt báo thức dựa vào giờ đồng hồ nếu có thể.

    Việc lặp lại chuông báo dựa trên thời gian kích hoạt chính xác sẽ không điều chỉnh được theo tỷ lệ. Sử dụng ELAPSED_REALTIME nếu có thể. Chuông báo khác được mô tả chi tiết hơn trong phần sau.