วิธีลบการเรียกกลับทั้งหมดจากตัวจัดการได้อย่างไร


223

ฉันมีHandlerจากกิจกรรมย่อยของฉันที่ถูกเรียกโดยหลักกิจกรรม ตัวจัดการนี้ถูกใช้โดยคลาสย่อยของpostDelayRunnables บางตัวและฉันไม่สามารถจัดการมันได้ ตอนนี้ในonStopเหตุการณ์ฉันต้องลบพวกเขาก่อนที่จะจบกิจกรรม (อย่างใดฉันเรียกว่าfinish()แต่มันยังคงโทรอีกครั้งและอีกครั้ง) มีอยู่แล้วเพื่อลบการเรียกกลับทั้งหมดจากตัวจัดการ

คำตอบ:


523

จากประสบการณ์ของฉันการเรียกใช้งานได้ดีมาก!

handler.removeCallbacksAndMessages(null);

ในเอกสารสำหรับการลบการเรียกคืนและข้อความแจ้งว่า ...

ลบโพสต์ที่รอดำเนินการของการเรียกกลับและข้อความที่ส่งซึ่ง obj คือโทเค็น หากเป็นโทเค็นการnullโทรกลับและข้อความทั้งหมดจะถูกลบ


2
@ Malachiasz ฉันคิดว่าฉันจะใช้มันใน onStop หรือ onPause เพื่อให้แน่ใจว่าไม่มีการจัดการข้อความหลังจากที่กิจกรรมได้สูญเสียการโฟกัส แต่ขึ้นอยู่กับสิ่งที่ต้องทำเมื่อโทรกลับ / ส่งข้อความ
บอย

1
ฉันเชื่อว่าฉันเคยเห็น NPE มาก่อนในโทรศัพท์บางรุ่นเมื่อทำสิ่งนี้ แต่มันก็ไม่นาน
Matt Wolfe

3
ฉันมีปัญหาบางอย่างกับremoveCallbacksAndMessages(null)จะไม่ลบการเรียกกลับของฉัน เมื่อฉันต้องการที่จะหยุดรับ Callbacks, ฉันต้องการโทรhandler.removeCallbacksAndMessages(null)และการตั้งค่าของฉันที่จะจัดการ null แต่ตั้งแต่ผมยังจะได้รับการติดต่อกลับผมพบ NPE handler.postDelayed()เมื่อฉันต้องการที่จะห่วงกับ
Snaker

@Snaker คุณแก้ไขปัญหาของคุณหรือยัง ฉันมีปัญหาเดียวกันกับที่ Handler.Callback ถูกเรียกแม้หลังจากลบการโทรกลับและข้อความโดยตั้งค่าเป็นศูนย์
ShrimpCrackers

1
@ShrimpCrackers ฉันพบว่าการเก็บอินสแตนซ์ของ runnable ของคุณและการใช้yourHandler.removeCallbacks(yourRunnable)นั้นน่าเชื่อถือที่สุด ยังคงใช้อยู่ในปัจจุบัน
Snaker

19

สำหรับการใด ๆ ที่เฉพาะเจาะจงเช่นการโทรRunnable Handler.removeCallbacks()โปรดทราบว่าจะใช้Runnableอินสแตนซ์ของตัวเองเพื่อตรวจสอบว่าการเรียกกลับเพื่อถอนการลงทะเบียนดังนั้นถ้าคุณกำลังสร้างอินสแตนซ์ใหม่ทุกครั้งที่โพสต์จะทำให้คุณต้องให้แน่ใจว่าคุณมีการอ้างอิงถึงที่แน่นอนRunnableที่จะยกเลิก ตัวอย่าง:

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

คุณสามารถโทรmyHandler.postDelayed(myRunnable, x)เพื่อโพสต์การโทรกลับอีกครั้งไปยังคิวข้อความที่สถานที่อื่น ๆ ในรหัสของคุณและลบการโทรกลับที่ค้างอยู่ทั้งหมดด้วยmyHandler.removeCallbacks(myRunnable)

น่าเสียดายที่คุณไม่สามารถ "ล้าง" ทั้งหมดMessageQueueสำหรับ a Handlerได้แม้ว่าคุณจะร้องขอMessageQueueวัตถุที่เกี่ยวข้องเนื่องจากวิธีการเพิ่มและลบรายการได้รับการป้องกันด้วยแพ็คเกจ (เฉพาะคลาสภายในแพ็คเกจ android.os เท่านั้นที่สามารถเรียกได้) คุณอาจต้องสร้างHandlerคลาสย่อยบาง ๆเพื่อจัดการรายการRunnables เนื่องจากโพสต์ / ดำเนินการ ... หรือดูกระบวนทัศน์อื่นเพื่อส่งข้อความของคุณระหว่างกันActivity

หวังว่าจะช่วย!


ขอบคุณฉันรู้ แต่ฉันมี Runnable มากมายในหลาย ๆ คลาสย่อยและจัดการพวกมันทั้งหมดเป็นงานที่ยิ่งใหญ่! อย่างไรก็ตามมีการลบออกทั้งหมดในเหตุการณ์ onStop () หรือไม่
Luke Vo

เข้าใจฉันปรับปรุงคำตอบด้วยข้อมูลอีกเล็กน้อย รุ่นสั้น ๆ คือคุณจะไม่สามารถเรียกวิธีการในวงกว้างล้างคิวข้อความของตัวจัดการ ...
Devunwired

8

หากคุณไม่มีการอ้างอิง Runnable ในการโทรกลับครั้งแรกรับ obj ของข้อความและใช้removeCallbacksAndMessages ()เพื่อลบการเรียกกลับที่เกี่ยวข้องทั้งหมด


6

กำหนดตัวจัดการใหม่และเรียกใช้:

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

โทรโพสต์ล่าช้า:

handler.postDelayed(runnable, sleep_time);

ลบการโทรกลับของคุณจากตัวจัดการของคุณ:

handler.removeCallbacks(runnable);

3

โปรดทราบว่าควรกำหนดขอบเขต a Handlerและ a Runnableในคลาสเพื่อให้สร้างขึ้นหนึ่งครั้ง removeCallbacks(Runnable)ทำงานได้อย่างถูกต้องเว้นแต่จะกำหนดไว้หลายครั้ง โปรดดูตัวอย่างต่อไปนี้เพื่อความเข้าใจที่ดีขึ้น:

วิธีที่ไม่ถูกต้อง:

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

หากคุณเรียกonClick(..)ใช้เมธอดคุณจะไม่หยุดdoIt()การเรียกใช้เมธอดก่อนที่จะเรียก เพราะแต่ละครั้งที่สร้างnew Handlerและnew Runnableกรณี ด้วยวิธีนี้คุณสูญเสียการอ้างอิงที่จำเป็นซึ่งเป็นของตัวจัดการและอินสแตนซ์ที่รันได้

วิธีที่ถูกต้อง:

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

ด้วยวิธีนี้คุณจะไม่สูญเสียการอ้างอิงที่แท้จริงและremoveCallbacks(runnable)ทำงานได้สำเร็จ

ประโยคที่สำคัญคือ'กำหนดให้เป็นระดับโลกในด้านของคุณActivityหรือFragmentสิ่งที่คุณใช้'


1

ในฐานะที่josh527กล่าวว่าhandler.removeCallbacksAndMessages(null);สามารถทำงานได้
แต่ทำไม
หากคุณดูซอร์สโค้ดคุณสามารถเข้าใจได้ชัดเจนยิ่งขึ้น มีวิธีการ 3 ชนิดในการลบการเรียกกลับ / ข้อความออกจากตัวจัดการ (MessageQueue):

  1. ลบโดยการโทรกลับ (และโทเค็น)
  2. ลบโดยข้อความอะไร (และโทเค็น)
  3. ลบโดยโทเค็น

Handler.java (ปล่อยวิธีการโอเวอร์โหลดบางอย่างไว้)

/**
 * 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 ทำงานจริง:

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;
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.