Làm thế nào để loại bỏ tất cả các cuộc gọi lại từ một Handler?


222

Tôi có một Trình xử từ Hoạt động phụ của mình được gọi là Hoạt động chính . Trình xử lý này được các lớp con sử dụng cho postDelaymột số Runnables và tôi không thể quản lý chúng. Bây giờ, trong onStopsự kiện, tôi cần xóa chúng trước khi kết thúc Hoạt động (bằng cách nào đó tôi đã gọi finish(), nhưng nó vẫn gọi đi gọi lại). Có cách nào để loại bỏ tất cả các cuộc gọi lại từ Handler không?

Câu trả lời:


522

Theo kinh nghiệm của tôi, việc này rất hiệu quả!

handler.removeCallbacksAndMessages(null);

Trong các tài liệu cho removeCallbacksAndMessages có ghi ...

Xóa mọi bài đăng đang chờ xử lý của các cuộc gọi lại và gửi tin nhắn có obj là mã thông báo. Nếu mã thông báo là null, tất cả các cuộc gọi lại và tin nhắn sẽ bị xóa.


2
@Malachiasz Tôi nghĩ rằng tôi sẽ sử dụng nó trong onStop hoặc onPause, để đảm bảo không có tin nhắn nào được xử lý sau khi hoạt động bị mất tập trung. Nhưng tùy thuộc vào những gì cần được thực hiện khi cuộc gọi lại / tin nhắn được kích hoạt
Boy

1
Tôi tin rằng tôi đã thấy NPE trước đây trên một số điện thoại khi thực hiện việc này nhưng nó đã được một lúc.
Matt Wolfe

3
Tôi đã có một số vấn đề với việc removeCallbacksAndMessages(null)không loại bỏ một số cuộc gọi lại của tôi. Khi tôi muốn ngừng nhận cuộc gọi lại, tôi sẽ gọi handler.removeCallbacksAndMessages(null)và đặt trình xử lý của mình thành null, nhưng vì tôi vẫn nhận được cuộc gọi lại, tôi sẽ gặp NPE khi tôi muốn lặp lại handler.postDelayed().
Snaker

@Snaker Bạn đã giải quyết vấn đề của mình chưa? Tôi gặp vấn đề tương tự khi Handler.Callback được gọi ngay cả sau khi xóa cuộc gọi lại và tin nhắn bằng cách đặt null.
Tôm Crackers

1
@ShaleighCrackers Tôi phát hiện ra việc giữ một ví dụ về khả năng chạy của bạn và sử dụng yourHandler.removeCallbacks(yourRunnable)là đáng tin cậy nhất. Vẫn đang sử dụng ngày hôm nay.
Snaker

19

Đối với bất kỳ Runnabletrường hợp cụ thể, gọi Handler.removeCallbacks(). Lưu ý rằng nó sử dụng Runnablechính thể hiện để xác định các cuộc gọi lại để hủy đăng ký, vì vậy nếu bạn đang tạo một phiên bản mới mỗi khi bài viết được thực hiện, bạn cần đảm bảo rằng bạn có tham chiếu đến chính xác Runnableđể hủy. Thí dụ:

Handler myHandler = new Handler();
Runnable myRunnable = new Runnable() {
    public void run() {
        //Some interesting task
    }
};

Bạn có thể gọi myHandler.postDelayed(myRunnable, x)để gửi một cuộc gọi lại khác vào hàng đợi tin nhắn ở những nơi khác trong mã của bạn và xóa tất cả các cuộc gọi lại đang chờ xử lý vớimyHandler.removeCallbacks(myRunnable)

Thật không may, bạn không thể đơn giản "xóa" toàn bộ MessageQueuecho a Handler, ngay cả khi bạn yêu cầu MessageQueueđối tượng được liên kết với nó bởi vì các phương thức thêm và xóa các mục được bảo vệ gói (chỉ các lớp trong gói android.os có thể gọi chúng). Bạn có thể phải tạo một Handlerlớp con mỏng để quản lý danh sách các Runnables khi chúng được đăng / thực thi ... hoặc xem xét một mô hình khác để truyền tin nhắn của bạn giữa mỗi danh sáchActivity

Mong rằng sẽ giúp!


Cảm ơn, tôi biết điều đó. Nhưng tôi có rất nhiều Runnable trong nhiều lớp học phụ, và quản lý tất cả chúng là một tác phẩm tuyệt vời! Có cách nào để xóa tất cả chúng, trong sự kiện onStop () không?
Luke Võ

Hiểu được, tôi cập nhật câu trả lời với một chút thông tin. Phiên bản ngắn là bạn không thể gọi một phương thức để xóa rộng hàng đợi tin nhắn của Handler ...
Devunwired

8

Nếu bạn không có các tham chiếu Runnable, trong lần gọi lại đầu tiên, hãy nhận thông báo và sử dụng removeCallbacksAndMessages () để xóa tất cả các cuộc gọi lại liên quan.


6

Xác định một trình xử lý mới và có thể chạy được:

private Handler handler = new Handler(Looper.getMainLooper());
private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            // Do what ever you want
        }
    };

Cuộc gọi bị trì hoãn:

handler.postDelayed(runnable, sleep_time);

Xóa cuộc gọi lại khỏi trình xử lý của bạn:

handler.removeCallbacks(runnable);

3

Xin lưu ý rằng người ta phải định nghĩa a Handlervà a Runnabletrong phạm vi lớp, để nó được tạo một lần. removeCallbacks(Runnable)hoạt động chính xác trừ khi người ta định nghĩa chúng nhiều lần. Vui lòng xem các ví dụ sau để hiểu rõ hơn:

Cách không chính xác:

    public class FooActivity extends Activity {
           private void handleSomething(){
                Handler handler = new Handler();
                Runnable runnable = new Runnable() {
                   @Override
                   public void run() {
                      doIt();
                  }
               };
              if(shouldIDoIt){
                  //doIt() works after 3 seconds.
                  handler.postDelayed(runnable, 3000);
              } else {
                  handler.removeCallbacks(runnable);
              }
           }

          public void onClick(View v){
              handleSomething();
          }
    } 

Nếu bạn gọi onClick(..)phương thức, bạn không bao giờ dừng doIt()phương thức gọi trước khi nó gọi. Bởi vì mỗi lần tạo new Handlernew Runnabletrường hợp. Theo cách này, bạn đã mất các tài liệu tham khảo cần thiết thuộc về các trường hợp xử lýcó thể chạy được .

Cách chính xác:

 public class FooActivity extends Activity {
        Handler handler = new Handler();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                doIt();
            }
        };
        private void handleSomething(){
            if(shouldIDoIt){
                //doIt() works after 3 seconds.
                handler.postDelayed(runnable, 3000);
            } else {
                handler.removeCallbacks(runnable);
            }
       }

       public void onClick(View v){
           handleSomething();
       }
 } 

Theo cách này, bạn không bị mất tài liệu tham khảo thực tế và removeCallbacks(runnable)hoạt động thành công.

Câu quan trọng là 'xác định chúng là toàn cầu trong bạn Activityhoặc Fragmentnhững gì bạn sử dụng' .


1

Như đã josh527nói, handler.removeCallbacksAndMessages(null);có thể làm việc.
Nhưng tại sao?
Nếu bạn có một cái nhìn vào mã nguồn, bạn có thể hiểu nó rõ ràng hơn. Có 3 loại phương thức để loại bỏ các cuộc gọi lại / tin nhắn khỏi trình xử lý (MessageQueue):

  1. xóa bằng cách gọi lại (và mã thông báo)
  2. xóa bằng tin nhắn.what (và mã thông báo)
  3. xóa bằng mã thông báo

Handler.java (để lại một số phương thức quá tải

/**
 * Remove any pending posts of Runnable <var>r</var> with Object
 * <var>token</var> that are in the message queue.  If <var>token</var> is null,
 * all callbacks will be removed.
 */
public final void removeCallbacks(Runnable r, Object token)
{
    mQueue.removeMessages(this, r, token);
}

/**
 * Remove any pending posts of messages with code 'what' and whose obj is
 * 'object' that are in the message queue.  If <var>object</var> is null,
 * all messages will be removed.
 */
public final void removeMessages(int what, Object object) {
    mQueue.removeMessages(this, what, object);
}

/**
 * Remove any pending posts of callbacks and sent messages whose
 * <var>obj</var> is <var>token</var>.  If <var>token</var> is null,
 * all callbacks and messages will be removed.
 */
public final void removeCallbacksAndMessages(Object token) {
    mQueue.removeCallbacksAndMessages(this, token);
}

MessageQueue.java thực hiện công việc thực sự:

void removeMessages(Handler h, int what, Object object) {
    if (h == null) {
        return;
    }

    synchronized (this) {
        Message p = mMessages;

        // Remove all messages at front.
        while (p != null && p.target == h && p.what == what
               && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }

        // Remove all messages after front.
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && n.what == what
                    && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}

void removeMessages(Handler h, Runnable r, Object object) {
    if (h == null || r == null) {
        return;
    }

    synchronized (this) {
        Message p = mMessages;

        // Remove all messages at front.
        while (p != null && p.target == h && p.callback == r
               && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }

        // Remove all messages after front.
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && n.callback == r
                    && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}

void removeCallbacksAndMessages(Handler h, Object object) {
    if (h == null) {
        return;
    }

    synchronized (this) {
        Message p = mMessages;

        // Remove all messages at front.
        while (p != null && p.target == h
                && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }

        // Remove all messages after front.
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}
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.