Bagaimana cara menghapus semua callback dari Handler?

222

Saya memiliki Handler dari sub-Aktivitas saya yang dipanggil oleh Activity utama . Handler ini digunakan oleh sub-kelas ke postDelaybeberapa Runnables, dan saya tidak bisa mengelolanya. Sekarang, dalam onStopacara tersebut, saya harus menghapusnya sebelum menyelesaikan Kegiatan (entah bagaimana saya menelepon finish(), tetapi masih memanggil lagi dan lagi). Apakah ada cara untuk menghapus semua callback dari Handler?

Luke Vo
sumber

Jawaban:

522

Dalam pengalaman saya menyebut ini bekerja sangat baik!

handler.removeCallbacksAndMessages(null);

Dalam dokumen untuk removeCallbacksAndMessages dikatakan ...

Hapus posting panggilan balik yang ditangguhkan dan pesan terkirim yang keberatannya token. Jika token adalah null, semua panggilan balik dan pesan akan dihapus.

josh527
sumber
2
@Malachiasz Saya pikir saya akan menggunakannya di onStop atau onPause, untuk memastikan tidak ada pesan yang ditangani setelah aktivitas kehilangan fokus. Tetapi tergantung pada apa yang perlu dilakukan ketika panggilan balik / pesan dipecat
Boy
1
Saya percaya saya pernah melihat NPE sebelumnya di beberapa ponsel ketika melakukan ini, tetapi sudah beberapa saat.
Matt Wolfe
3
Saya memiliki beberapa masalah dengan removeCallbacksAndMessages(null)tidak akan menghapus beberapa panggilan balik saya. Ketika saya ingin berhenti menerima Callback, saya akan menelepon handler.removeCallbacksAndMessages(null)dan mengatur handler saya ke nol, tetapi karena saya masih akan mendapatkan callback, saya akan menemui NPE ketika saya ingin mengulanginya handler.postDelayed().
Snaker
@Snaker Sudahkah Anda menyelesaikan masalah Anda? Saya mengalami masalah yang sama dengan Handler.Callback dipanggil bahkan setelah menghapus panggilan balik dan pesan dengan menetapkan nol.
ShrimpCrackers
1
@ ShrimpCrackers Saya menemukan menjaga instance dari runnable Anda dan menggunakan yourHandler.removeCallbacks(yourRunnable)adalah yang paling dapat diandalkan. Masih menggunakannya hari ini.
Snaker
19

Untuk setiap Runnablecontoh spesifik , hubungi Handler.removeCallbacks(). Perhatikan bahwa ia menggunakan Runnableinstance itu sendiri untuk menentukan callback mana yang dibatalkan pendaftarannya, jadi jika Anda membuat instance baru setiap kali posting dibuat, Anda perlu memastikan bahwa Anda memiliki referensi untuk membalas tepat Runnable. Contoh:

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

Anda dapat menelepon myHandler.postDelayed(myRunnable, x)untuk mengirim panggilan balik lain ke antrian pesan di tempat lain dalam kode Anda, dan menghapus semua panggilan balik yang tertunda denganmyHandler.removeCallbacks(myRunnable)

Sayangnya, Anda tidak bisa begitu saja "menghapus" keseluruhan MessageQueueuntuk Handler, bahkan jika Anda membuat permintaan untukMessageQueue objek yang terkait dengannya karena metode untuk menambah dan menghapus item dilindungi paket (hanya kelas dalam paket android.os yang dapat memanggilnya). Anda mungkin harus membuat Handlersubclass tipis untuk mengelola daftar Runnableketika mereka diposkan / dieksekusi ... atau melihat paradigma lain untuk meneruskan pesan Anda di antara masing-masingActivity

Semoga Itu Membantu!

Devunwired
sumber
Terima kasih, saya tahu itu. Tapi saya punya banyak Runnable di banyak sub-kelas, dan mengelola semuanya adalah pekerjaan epik! Apakah ada cara untuk menghapus semuanya, di acara onStop ()?
Luke Vo
Dipahami, saya memperbarui jawabannya dengan sedikit lebih banyak informasi. Versi
singkatnya
8

Jika Anda tidak memiliki referensi Runnable, pada panggilan balik pertama, dapatkan keberatan dari pesan tersebut, dan gunakan removeCallbacksAndMessages () untuk menghapus semua panggilan balik terkait.

alphazero
sumber
6

Tetapkan handler baru dan runnable:

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

Pos panggilan tertunda:

handler.postDelayed(runnable, sleep_time);

Hapus panggilan balik Anda dari penangan Anda:

handler.removeCallbacks(runnable);
savepopulation
sumber
3

Harap dicatat bahwa seseorang harus mendefinisikan ruang lingkup a Handlerdan a Runnabledi kelas, sehingga itu dibuat sekali. removeCallbacks(Runnable)berfungsi dengan benar kecuali seseorang mendefinisikannya beberapa kali. Silakan lihat contoh-contoh berikut untuk pemahaman yang lebih baik:

Cara yang salah:

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

Jika Anda memanggil onClick(..)metode, Anda tidak pernah berhenti doIt()memanggil metode sebelum memanggilnya. Karena setiap waktu menciptakan new Handlerdan memberi new Runnablecontoh. Dengan cara ini, Anda kehilangan referensi yang diperlukan milik instance handler dan runnable .

Jalan yang benar :

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

Dengan cara ini, Anda tidak kehilangan referensi aktual dan removeCallbacks(runnable) bekerja dengan sukses.

Kalimat utama adalah 'mendefinisikannya sebagai global dalam Anda Activityatau Fragmentapa yang Anda gunakan' .

oguzhan
sumber
1

Seperti yang josh527dikatakan, handler.removeCallbacksAndMessages(null);bisa bekerja.
Tapi kenapa?
Jika Anda melihat kode sumbernya, Anda dapat memahaminya dengan lebih jelas. Ada 3 jenis metode untuk menghapus panggilan balik / pesan dari penangan (MessageQueue):

  1. hapus dengan callback (dan token)
  2. hapus dengan message.what (dan token)
  3. hapus dengan token

Handler.java (tinggalkan beberapa metode kelebihan)

/**
 * 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 melakukan pekerjaan nyata:

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;
        }
    }
}
JamesRobert
sumber