Android AlarmManager - RTC_WAKEUP đấu với ELAPSED_REALTIME_WAKEUP


87

Ai đó có thể giải thích cho tôi sự khác biệt giữa AlarmManager.RTC_WAKEUPAlarmManager.ELAPSED_REALTIME_WAKEUP? Tôi đã đọc tài liệu nhưng vẫn không thực sự hiểu ý nghĩa của việc sử dụng cái này hơn cái kia.

Mã ví dụ:

    alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 
                     scheduledAlarmTime, 
                     pendingIntent);

    alarmManager.set(AlarmManager.RTC_WAKEUP, 
                     scheduledAlarmTime, 
                     pendingIntent);

Hai dòng mã sẽ thực thi khác nhau như thế nào? Khi nào thì hai dòng mã đó sẽ thực thi tương đối với nhau?

Tôi đánh giá cao sự giúp đỡ của bạn.

Câu trả lời:


140

AlarmManager.ELAPSED_REALTIME_WAKEUP loại được sử dụng để kích hoạt cảnh báo kể từ thời điểm khởi động:

alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 600000, pendingIntent);

sẽ thực sự làm cho báo thức tắt 10 phút sau khi thiết bị khởi động .

Có bộ đếm thời gian bắt đầu chạy khi thiết bị khởi động để đo thời gian hoạt động của thiết bị và đây là loại kích hoạt báo thức của bạn theo thời gian hoạt động của thiết bị.

Trong khi, AlarmManager.RTC_WAKEUPsẽ kích hoạt báo thức theo thời gian của đồng hồ. Ví dụ nếu bạn làm:

long thirtySecondsFromNow = System.currentTimeMillis() + 30 * 1000;
alarmManager.set(AlarmManager.RTC_WAKEUP, thirtySecondsFromNow , pendingIntent);

mặt khác, điều này sẽ kích hoạt báo thức 30 giây kể từ bây giờ .

AlarmManager.ELAPSED_REALTIME_WAKEUPloại ít được sử dụng so với AlarmManager.RTC_WAKEUP.


1
Đó là những gì tôi nghĩ. Tôi chỉ cần một số xác nhận. Vì vậy, nếu tôi đã làm điều gì đó như sau: alertManager.set (AlarmManager.ELAPSED_REALTIME_WAKEUP, System.currentTimeMills () + 30 * 1000, pendingIntent); những điều kỳ lạ có thể xảy ra (đó là những gì tôi đã có cho đến khi tôi nhận thấy rằng nó không hoạt động như tôi nghĩ.
Camille Sévigny

2
Xin lưu ý rằng mã phải được System.currentTimeMillis()thay vì System.currentTimeMills():)
HasanAboShally

17
"Loại AlarmManager.ELAPSED_REALTIME_WAKEUP hiếm khi được sử dụng so với AlarmManager.RTC_WAKEUP." Đây là suy đoán và lời khuyên tồi. Theo tài liệu: developer.android.com/training/scheduling/alarms.html "Nếu bạn chỉ cần báo thức báo động vào một khoảng thời gian cụ thể (ví dụ: nửa giờ một lần), hãy sử dụng một trong các kiểu thời gian thực đã trôi qua. Trong nói chung, đây là sự lựa chọn tốt hơn. "
Jared Kells

1
Câu trả lời của @ mborsuk tốt hơn và sửa lại câu trả lời này: Bạn không nên sử dụng RTC trong thời gian đã trôi qua, như for thirtySecondsFromNow.
Micha F.

2
Giải thích khá hay :)! Tôi muốn thêm một nhận xét quan trọng: liên quan đến các tài liệu chính thức từ Android, việc sử dụng "AlarmManager.ELAPSED_REALTIME_WAKEUP" có thể là một điều gì đó thú vị khi nói đến một ứng dụng kích hoạt các yêu cầu HTTP đến một máy chủ và bạn không muốn tạo bất kỳ tải trên máy chủ web của bạn. Hãy nghĩ về hàng nghìn thiết bị Android, thực hiện GET trên máy chủ web, lúc 10:30 tối: do thực tế hoạt động theo thời gian khởi động của thiết bị, "AlarmManager.ELAPSED_REALTIME_WAKEUP" có thể khiến "hàng nghìn" thiết bị này kích hoạt yêu cầu, không đồng thời tránh tải :).
ivanleoncz

107

Mặc dù câu trả lời hiện được chấp nhận và được bình chọn cao, nhưng các loại AlarmManager.ELAPSED_REALTIME * cùng với SystemClock.elapsedRealtime () luôn đáng tin cậy hơn so với đồng hồ RTC về báo thức và thời gian.

Sử dụng ELAPSED_REALTIME_WAKEUP với AlarmManager sẽ dựa trên đồng hồ đơn âm bắt đầu từ thời gian khởi động " và tiếp tục đánh dấu ngay cả khi CPU đang ở chế độ tiết kiệm năng lượng, do đó, đây là cơ sở khuyến nghị cho mục đích chung định thời gian ". Vì thế,

alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()
                 + 60*1000, pendingIntent);

sẽ kích hoạt PendingIntent của bạn trong 1 phút (60 * 1000 mili giây).

Trong khi, AlarmManager.RTC_WAKEUP dành cho thời gian "tường" tiêu chuẩn tính bằng mili giây kể từ kỷ nguyên. Vì thế,

alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
                 + 60*10000, pendingIntent);

cũng có thể kích hoạt cảnh báo 60 giây kể từ bây giờ, nhưng không đáng tin cậy, vì như đã lưu ý trong tài liệu SystemClock :

Đồng hồ treo tường có thể được đặt bởi người dùng hoặc mạng điện thoại (xem setCurrentTimeMillis (dài)), do đó, thời gian có thể nhảy ngược hoặc lùi không thể đoán trước. Đồng hồ này chỉ nên được sử dụng khi tương ứng với ngày và giờ trong thế giới thực là quan trọng, chẳng hạn như trong ứng dụng lịch hoặc đồng hồ báo thức. Các phép đo khoảng thời gian hoặc thời gian đã trôi qua nên sử dụng đồng hồ khác. Nếu bạn đang sử dụng System.currentTimeMillis (), hãy cân nhắc nghe các chương trình phát ACTION_TIME_TICK, ACTION_TIME_CHANGED và ACTION_TIMEZONE_CHANGED Intent để biết thời gian thay đổi.

Ngoài ra, câu hỏi chỉ đề cập đến cảnh báo * _WAKEUP nhưng hãy xem thêm tài liệu AlarmManager về vấn đề đó để đảm bảo bạn hiểu những gì mà báo thức đánh thức và không đánh thức cung cấp.


Phản hồi của bạn rất tuyệt, nhưng trong ứng dụng của tôi, tôi cần đặt báo thức trùng với ngày / giờ trong thế giới thực chứ không chỉ 60 giây kể từ bây giờ. Trong trường hợp đó, RTC_WAKEUP là giải pháp tốt nhất cho tôi.
Camille Sévigny

8
Điều đó tốt nhưng đây là một câu trả lời chính xác hơn cho câu hỏi và chỉnh sửa những điều trong câu trả lời hiện được chấp nhận.
mborsuk

+1 cho thông tin này, nhưng lưu ý rằng ở elapsedRealtime()dưới SystemClockthay vì System. EDIT: ... giới hạn bỏ phiếu hàng ngày đạt ...
Jorge Fuentes González

Đây là một trong những câu trả lời tốt nhất trong vấn đề đó. Tôi đang tìm kiếm một cây kim trong đống cỏ khô cao 50m (hay còn gọi là "một lỗi trong mã của tôi!"), Và câu trả lời của bạn đã dẫn tôi đến thẳng cái kim!
AlxDroidDev

17

Chỉ là một ghi chú. Bạn có thể nhận được một phần nghìn thời gian hoạt động gọi:

long uptimeMillis =  SystemClock.elapsedRealtime();

Vì vậy, nếu bạn muốn kích hoạt báo thức 30 giây kể từ bây giờ và bạn muốn sử dụng đồng hồ thời gian hoạt động thay vì đồng hồ bình thường, bạn có thể làm:

long thirtySecondsFromNow =  SystemClock.elapsedRealtime() + 30 * 1000;
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, thirtySecondsFromNow, pendingIntent);

Bất cứ khi nào bạn muốn kiểm tra một số thời gian đã trôi qua thay vì một ngày / giờ cụ thể, tốt nhất nên sử dụng thời gian hoạt động. Đó là bởi vì thời gian hiện tại do người dùng đặt trong thiết bị có thể thay đổi nếu người dùng thay đổi nó bằng cách sử dụng cài đặt.


3
+1 để chỉ ra để sử dụng "bất cứ khi nào bạn muốn kiểm tra thời gian đã trôi qua". Hoàn toàn có lý.
sulai

Một lần nữa chỉ để nhấn mạnh: sự hiểu biết của tôi là điều này chắc chắn sẽ hoạt động trong 30 giây kể từ bây giờ trừ khi có điều gì đó xảy ra như thiết bị tắt. Cụ thể, tôi nghĩ vấn đề với việc sử dụng RTC_WAKEUP là về lý thuyết, 15 giây kể từ bây giờ, có thể nó sẽ giống như 1 giờ sáng vào thứ Bảy nào đó gần cuối tháng 10 và đồng hồ hệ thống quay ngược lại 1 giờ (ở Mỹ và phần lớn Châu Âu, ít nhất), do đó, báo thức sẽ không thực sự kích hoạt cho đến 1 giờ 30 giây sau khi được đặt.
Yannick

2

Tôi đã lập trình vấn đề này trong dự án của riêng mình theo cách này. trong mã dưới đây tôi đang sử dụng

AlarmManager.ELAPSED_REALTIME_WAKEUP

để đặt báo thức vào một thời điểm cụ thể. biến 'Ý định tên' được sử dụng trong Bộ lọc Ý định để nhận cảnh báo này. bởi vì tôi đang kích hoạt nhiều cảnh báo kiểu này. khi tôi hủy tất cả các báo thức. tôi sử dụng phương pháp hủy bỏ. đưa ra ở dưới cùng.

// để giữ báo thức và hủy khi cần

     public static ArrayList<String> alarmIntens = new ArrayList<String>();

//

    public static String setAlarm(int hour, int minutes, long repeatInterval,
        final Context c) {
    /*
     * to use elapsed realTime monotonic clock, and fire alarm at a specific time
     * we need to know the span between current time and the time of alarm.
     * then we can add this span to 'elapsedRealTime' to fire the alarm at that time
     * this way we can get alarms even when device is in sleep mood
    */
    Time nowTime = new Time();
    nowTime.setToNow();
    Time startTime = new Time(nowTime);
    startTime.hour = hour;
    startTime.minute = minutes;
    //get the span from current time to alarm time 'startTime'
    long spanToStart = TimeUtils.spanInMillis(nowTime, startTime);
    //
    intentName = "AlarmBroadcast_" + nowTime.toString();
    Intent intent = new Intent(intentName);
    alarmIntens.add(intentName);
    PendingIntent pi = PendingIntent.getBroadcast(c, alarms++, intent,
            PendingIntent.FLAG_UPDATE_CURRENT);
    //
    AlarmManager am = (AlarmManager) c
            .getSystemService(Context.ALARM_SERVICE);
    //adding span to elapsedRealTime
    long elapsedRealTime = SystemClock.elapsedRealtime();
    Time t1 = new Time();
    t1.set(elapsedRealTime);
    t1.second=0;//cut inexact timings, seconds etc
    elapsedRealTime = t1.toMillis(true);

    if (!(repeatInterval == -1))
        am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                elapsedRealTime + spanToStart, repeatInterval, pi);
    else
        am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, elapsedRealTime
                + spanToStart, pi);

Hàm span ở đâu đây:

 public static long spanInMillis(Time startTime, Time endTime) {
    long diff = endTime.toMillis(true) - startTime.toMillis(true);
    if (diff >= 0)
        return diff;
    else
        return AlarmManager.INTERVAL_DAY - Math.abs(diff);
}

chức năng hủy báo động là này.

public static void cancel(Context c) {
    AlarmManager am = (AlarmManager) c
            .getSystemService(Context.ALARM_SERVICE);
    // cancel all alarms
    for (Iterator<String> iterator = alarmIntens.iterator(); iterator
            .hasNext();) {
        String intentName = (String) iterator.next();
        // cancel
        Intent intent = new Intent(intentName);
        PendingIntent pi = PendingIntent.getBroadcast(c, 0, intent,
                PendingIntent.FLAG_UPDATE_CURRENT);
        am.cancel(pi);
        //
        iterator.remove();
    }
}

1

Một số lưu ý quan trọng khi chọn báo thức nào sẽ sử dụng : (dành cho ai đã đọc phiếu ủng hộ)

Các RTC_WAKEUP thung lung chết - sự thay đổi thời gian:
Nếu người dùng có thời gian thay đổi bằng tay để quá khứ báo động sẽ không đi off, và trong tương lai sẽ gây ra báo động để đi tắt ngay lập tức nếu nó quá khứ RTCdấu thời gian.
Không sử dụng cảnh báo này để thực hiện bất kỳ công việc xác minh / quan trọng nào của phía khách hàng vì nó có khả năng bị lỗi.

Các WAKEUP ý nghĩa (Marshmallow trở lên)
Nói chung - không nhiều. Sẽ không đánh thức thiết bị khi idlehoặc trong khi ở dozeđó alarmManager.setExactAndAllowWhileIdlehoặc alarmManager.setAndAllowWhileIdle( Ngủ gật & Chờ )

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.