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 thao tác chạy trong thời gian dài, chẳng hạn như bắt đầu một dịch vụ một lần mỗi ngày để tải thông tin dự báo thời tiết xuống.

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

  • Các tệp này cho phép bạn kích hoạt Ý định vào 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ử.

  • Các dịch vụ này hoạt động bên ngoài ứng dụng, vì vậy, bạn có thể sử dụng các dịch vụ này để kích hoạt sự kiện hoặc hành động ngay cả khi ứng dụng không chạy và ngay cả khi thiết bị đang ở trạng thái 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ẽ gửi chuông báo vào một 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 vòng một giờ kể từ thời gian kích hoạt đã cung cấp, trừ phi có bất kỳ quy định hạn chế nào về việc tiết kiệm pin đang có hiệu lực, chẳng hạn như trình tiết kiệm pin hoặc Chế độ nghỉ.

Đư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(), chuông báo sẽ không bao giờ kêu trước thời gian kích hoạt đã cung cấp. 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 việc gọi chuông báo không chính xác theo khung thời gian ít nhất 10 phút. Vì lý do này, các giá trị tham số windowLengthMillis trong 600000 sẽ bị cắt bớt thành 600000.

Cung cấp chuông báo lặp lại theo khoảng thời gian 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. Chuông báo tiếp theo thường kêu sau khi khoảng thời gian đã chỉ định trôi qua. Khoảng thời gian giữa hai lệnh gọi chuông báo liên tiếp có thể khác nhau.

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

Hệ thống sẽ gọi một chuông báo chính xác vào 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 chuô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ố phương thức tốt để xử lý các thao tác 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 trong 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 công việc định kỳ có giới hạn thời gian. 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 nhỏ nhất được 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 phương thức ở gần cuối danh sách sẽ thực hiện nhiều tác vụ quan trọng về thời gian hơn 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ả.

Sử dụng phương thức này để đặt chuông báo chính xác, trừ phi công việc của ứng dụng là rất quan trọng về mặt thời gian đố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ì người dùng có thể dễ dàng nhìn thấy các chuông báo này, nên 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ị sẽ tiêu thụ rất nhiều tài nguyên, chẳng hạn như thời lượng pin, đặc biệt là khi thiết bị ở chế độ tiết kiệm pin. 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 cảnh báo không chính xác bất cứ khi nào 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 sử dụng chuông báo chính xác phù hợp

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 đều báo hiệu cùng một chức năng, nhưng chúng được cấp theo cách khác nhau và hỗ trợ các trường hợp sử dụng khác nhau. Ứng dụng của bạn chỉ nên sử dụng chuông báo chính xác và khai báo quyền SCHEDULE_EXACT_ALARM hoặc USE_EXACT_ALARM nếu một hàm dành cho người dùng trong ứng dụng yêu cầu các hành động được tính giờ 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 của ứng dụng nhắm đến Android 13 (API cấp 33) trở lên. Nếu người dùng chuyển dữ liệu ứng dụng sang một thiết bị chạy Android 14 thông qua tác vụ sao lưu và khôi phục, thì 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ó quyền này, thì ứng dụng đó sẽ được cấp quyền trước khi thiết bị nâng cấp 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, người dùng phải cấp quyền SCHEDULE_EXACT_ALARM. 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 cho ứng dụng, ứng dụng sẽ dừng và tất cả chuông báo chính xác trong tương lai sẽ bị huỷ. Điều này cũng có nghĩa là giá trị do canScheduleExactAlarms() trả về vẫn hợp lệ trong toàn bộ vòng đời của ứng dụng.

Khi cấp quyền SCHEDULE_EXACT_ALARMS cho ứng dụng, hệ thống sẽ gửi thông báo truyền tin ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED cho ứng dụng đó. Ứ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 tương tự như những gì ứng dụng của bạn thực hiện khi nhận được thông báo truyền tin ACTION_BOOT_COMPLETED.

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. Trang quyền truy cập đặc biệt của ứng dụng "Chuông báo và lời nhắc" 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 chính xác.

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ủa bạn cần lên lịch chuông báo chính xác.
  2. Gọi một ý định bao gồm thao tác theo ý định ACTION_REQUEST_SCHEDULE_EXACT_ALARM.

Đặt chuông báo 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 tốt có thể làm tiêu hao pin và gây ra tải đáng kể cho 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 của chuông báo. Ví dụ: một lần mỗi ngày, mỗi giờ hoặc mỗi 5 phút.

  • Một ý định đang chờ xử lý sẽ 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 truyền FLAG_NO_CREATE vào PendingIntent.getService() để lấy một thực thể của ý định (nếu có), sau đó truyề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ó hai loại đồng hồ chung cho chuông báo: "thời gian thực đã trôi qua" 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ủa bạn có phần phụ thuộc thời gian. Ví dụ: nếu có một khoảng thời gian giới hạn để thực hiện một 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 theo một khoảng thời gian cụ thể (ví dụ: sau 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 trong các loại đồng hồ theo 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ý vào thời điểm chỉ định nhưng không đánh thức thiết bị.

  • RTC_WAKEUP: Đánh thức thiết bị để kích hoạt ý định đang chờ xử lý vào thời điểm đã chỉ định.

Ví dụ về chuông báo 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 vào cùng một thời điểm:

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 quá trình tạo 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 phù hợp. 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à kích hoạt các chuông báo đó 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ể làm với 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. Hãy xem AlarmManager để biết danh sách đầy đủ.

Hủy chuông báo

Tuỳ thuộc vào ứng dụng, bạn có thể muốn thêm tính năng huỷ chuông báo. Để huỷ chuông báo, hãy gọi cancel() trên Trình quản lý chuông báo, truyền vào PendingIntent mà bạn không còn muốn 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 sẽ 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 chuông báo định kỳ 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 thu bằng cách lập trình sẽ 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ỉ

Các thiết bị chạy Android 6.0 (API cấp 23) hỗ trợ chế độ Ngủ 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ị ở chế độ Nghỉ Mọi chuông báo theo lịch sẽ 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 tạo để thực hiện công việc trong nền. Bạn có thể cho biết hệ thống nên ưu tiên 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 thao tác đồng bộ hoá dựa trên thời gian đồng hồ và mọi phiên bản của ứng dụng đồng bộ hoá lúc 23:00, thì tải trên máy chủ có thể dẫn đến độ trễ cao hoặc thậm chí là "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. "Công việc cục bộ" có nghĩa là mọi thao tác không truy cập vào máy chủ hoặc không yêu cầu dữ liệu từ máy chủ.

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

  • Hạn chế tối đa tần suất chuông báo.

  • Không đánh thức thiết bị khi không cần thiết (hành vi này do loại chuông báo xác định, như mô tả trong phần 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. Xin lưu ý rằng mặc dù setInexactRepeating() là một điểm cải tiến so với setRepeating(), nhưng vẫn có thể làm quá tải máy chủ nếu mọi phiên bản của một ứng dụng truy cập vào máy chủ cùng một lúc. Do đó, đối với các yêu cầu mạng, hãy thêm một số yếu tố ngẫu nhiên vào chuông báo, như đã thảo luận trước đó.

  • Tránh đặt chuông báo dựa trên thời gian đồ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ể. Các loại chuông báo được mô tả chi tiết hơn trong phần sau.