Lưu ý: Tôi đã thử các giải pháp khác nhau được viết ở đây trên StackOverflow (ví dụ ở đây ). Vui lòng không đóng cái này mà không kiểm tra xem giải pháp của bạn từ những gì bạn đã tìm thấy có hiệu quả hay không bằng cách sử dụng bài kiểm tra mà tôi đã viết dưới đây.
Lý lịch
Có một yêu cầu trên ứng dụng, đó là người dùng đặt lời nhắc được lên lịch vào một thời điểm cụ thể, vì vậy khi ứng dụng được kích hoạt vào thời điểm này, nó sẽ làm một cái gì đó rất nhỏ trong nền (chỉ một số thao tác truy vấn DB) và hiển thị thông báo đơn giản, để nói về lời nhắc nhở.
Trước đây, tôi đã sử dụng một mã đơn giản để đặt thứ gì đó được lên lịch vào một thời điểm tương đối cụ thể:
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val pendingIntent = PendingIntent.getBroadcast(context, requestId, Intent(context, AlarmReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
when {
VERSION.SDK_INT >= VERSION_CODES.KITKAT -> alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
else -> alarmManager.set(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
}
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("AppLog", "AlarmReceiver onReceive")
//do something in the real app
}
}
Sử dụng:
val timeToTrigger = System.currentTimeMillis() + java.util.concurrent.TimeUnit.MINUTES.toMillis(1)
setAlarm(this, timeToTrigger, 1)
Vấn đề
Bây giờ tôi đã thử nghiệm mã này trên các trình giả lập trên các phiên bản Android mới và trên Pixel 4 với Android 10 và dường như nó không kích hoạt hoặc có thể nó sẽ kích hoạt sau một thời gian rất dài kể từ những gì tôi cung cấp. Tôi nhận thức rõ về hành vi khủng khiếp đó một số OEM đã thêm vào để xóa ứng dụng khỏi các tác vụ gần đây, nhưng ứng dụng này có trên cả trình giả lập và thiết bị Pixel 4 (chứng khoán).
Tôi đã đọc trên các tài liệu về việc đặt báo thức, rằng nó bị hạn chế cho các ứng dụng để nó không xảy ra quá thường xuyên, nhưng điều này không giải thích cách đặt báo thức vào một thời điểm cụ thể và nó không giải thích làm thế nào mà ứng dụng Đồng hồ của Google thành công khi làm điều đó.
Không chỉ vậy, nhưng theo những gì tôi hiểu, nó nói rằng các hạn chế nên được áp dụng đặc biệt đối với trạng thái năng lượng thấp của thiết bị, nhưng trong trường hợp của tôi, tôi không có trạng thái này, trên cả thiết bị và trên trình giả lập. Tôi đã đặt báo thức để được kích hoạt sau khoảng một phút kể từ bây giờ.
Thấy rằng nhiều ứng dụng đồng hồ báo thức không còn hoạt động như trước đây, tôi nghĩ rằng có một cái gì đó còn thiếu trên các tài liệu. Ví dụ về các ứng dụng như vậy là ứng dụng Timely phổ biến đã được Google mua nhưng không bao giờ có bản cập nhật mới để xử lý các hạn chế mới và bây giờ người dùng muốn có lại. . Tuy nhiên, một số ứng dụng phổ biến hoạt động tốt, chẳng hạn như ứng dụng này .
Những gì tôi đã thử
Để kiểm tra xem báo thức có thực sự hoạt động không, tôi thực hiện các thử nghiệm này khi thử kích hoạt báo thức trong một phút kể từ bây giờ, sau khi cài đặt ứng dụng lần đầu tiên, tất cả trong khi thiết bị được kết nối với PC (để xem nhật ký):
- Kiểm tra khi ứng dụng ở phía trước, hiển thị cho người dùng. - mất 1-2 phút.
- Kiểm tra khi ứng dụng được gửi đến nền (ví dụ: sử dụng nút home) - mất khoảng 1 phút
- Kiểm tra khi tác vụ của ứng dụng đã bị xóa khỏi các tác vụ gần đây. - Tôi đã đợi hơn 20 phút và không thấy báo thức được kích hoạt, ghi vào nhật ký.
- Giống như số 3, nhưng cũng tắt màn hình. Có lẽ sẽ tệ hơn ...
Tôi đã cố gắng sử dụng những thứ tiếp theo, tất cả đều không hoạt động:
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
kết hợp của bất kỳ điều nào ở trên, với:
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) alarmManager.setWindow(AlarmManager.RTC_WAKEUP, 0, 60 * 1000L, pendingIntent)
Đã thử sử dụng một dịch vụ thay vì BroadcastReceiver. Cũng đã thử trên một quy trình khác nhau.
Đã thử làm cho ứng dụng bị bỏ qua khỏi tối ưu hóa pin (không giúp ích), nhưng vì các ứng dụng khác không cần đến nó, tôi cũng không nên sử dụng nó.
Đã thử sử dụng điều này:
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP)
alarmManager.setAlarmClock(AlarmManager.AlarmClockInfo(timeToTrigger, pendingIntent), pendingIntent)
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, timeToTrigger, pendingIntent)
- Đã thử có một dịch vụ sẽ kích hoạt onTaskRemond , để lên lịch lại báo thức ở đó, nhưng điều này cũng không giúp được gì (mặc dù dịch vụ vẫn hoạt động tốt).
Đối với ứng dụng Đồng hồ của Google, tôi không thấy có gì đặc biệt về nó ngoại trừ việc nó hiển thị thông báo trước khi được kích hoạt và tôi cũng không thấy nó trong phần "không được tối ưu hóa" của màn hình cài đặt tối ưu hóa pin.
Thấy rằng đây có vẻ là một lỗi, tôi đã báo cáo về điều này ở đây , bao gồm một dự án mẫu và video để hiển thị vấn đề.
Tôi đã kiểm tra trên nhiều phiên bản của trình giả lập và có vẻ như hành vi này bắt đầu từ API 27 (Android 8.1 - Oreo). Nhìn vào các tài liệu , tôi không thấy Báo động viên được đề cập, nhưng thay vào đó, nó được viết về nhiều công việc nền tảng khác nhau.
Những câu hỏi
Làm thế nào để chúng ta thiết lập một cái gì đó được kích hoạt tại một thời điểm tương đối chính xác ngày nay?
Làm thế nào mà các giải pháp trên không hoạt động nữa? Tôi có thiếu thứ gì không? Giấy phép? Có lẽ tôi nên sử dụng một Công nhân thay thế? Nhưng điều đó có nghĩa là nó có thể không kích hoạt đúng giờ không?
Làm thế nào để ứng dụng "Đồng hồ" của Google vượt qua tất cả những điều này và luôn luôn kích hoạt vào thời gian chính xác, ngay cả khi nó được kích hoạt chỉ một phút trước? Có phải chỉ vì nó là một ứng dụng hệ thống? Điều gì sẽ xảy ra nếu ứng dụng được cài đặt dưới dạng ứng dụng người dùng, trên thiết bị không tích hợp sẵn?
Nếu bạn nói rằng đó là bởi vì nó là một ứng dụng hệ thống, tôi đã tìm thấy một ứng dụng có thể kích hoạt một báo động gấp đôi trong vòng 2 phút, ở đây , mặc dù tôi nghĩ rằng nó có thể sử dụng một dịch vụ foreground đôi khi.
EDIT: tạo một kho lưu trữ Github nhỏ để thử ý tưởng tại đây .
EDIT: cuối cùng đã tìm thấy một mẫu có nguồn mở và không có vấn đề này. Đáng buồn là nó rất phức tạp và tôi vẫn cố gắng tìm ra điều gì làm cho nó khác biệt (và mã tối thiểu mà tôi nên thêm vào POC của mình) để cho phép báo thức của nó được lên lịch sau khi xóa ứng dụng khỏi các tác vụ gần đây