วัตถุประสงค์ของ Looper คืออะไรและจะใช้งานอย่างไร


454

ฉันใหม่สำหรับ Android ฉันต้องการรู้ว่าLooperชั้นเรียนทำอะไรและใช้อย่างไร ฉันอ่านเอกสารระดับ Android Looper แล้วแต่ฉันไม่สามารถเข้าใจได้อย่างสมบูรณ์ ฉันเคยเห็นมันในหลาย ๆ ที่ แต่ไม่เข้าใจวัตถุประสงค์ของมัน ใครสามารถช่วยฉันด้วยการกำหนดวัตถุประสงค์Looperและโดยให้เป็นตัวอย่างง่ายๆถ้าเป็นไปได้?


7
ฉันเพิ่งพบคำอธิบายอย่างละเอียดและชัดเจนเกี่ยวกับ Looper และการใช้งานบน Safari Books Online น่าเสียดายที่ฉันสงสัยว่าการเข้าถึงหากฟรีในเวลา จำกัด safaribooksonline.com/library/view/efficient-android-threading/…
Joe Lapp

1
บทความ Android และหน้าอ้างอิงต้องการให้คุณมีและทำความเข้าใจกับบทความก่อนหน้านี้ก่อนที่คุณจะสามารถเข้าใจบทความปัจจุบัน ฉันแนะนำให้คุณอ่านบทความกิจกรรมและบริการในคำแนะนำของ Api แล้วอ่าน Handler และ Looper นอกจากนี้ยังช่วยถ้าคุณมีความเข้าใจในสิ่งที่เป็นเธรด (ไม่ใช่เธรด Android แต่เป็นเธรดโดยทั่วไป ... เช่น POSIX)
FutureSci

1
ฉันพบบทความนี้มีประโยชน์: codetheory.in/…
Herman

คำตอบ:


396

Looper คืออะไร

Looper เป็นคลาสที่ใช้เรียกใช้ Messages (Runnables) ในคิว เธรดปกติไม่มีคิวดังกล่าวเช่นเธรดธรรมดาไม่มีคิวใด ๆ มันดำเนินการครั้งเดียวและหลังจากการดำเนินการวิธีการเสร็จสิ้นเธรดจะไม่เรียกใช้ข้อความอื่น (Runnable)

เราจะใช้คลาส Looper ได้ที่ไหน?

หากมีคนต้องการที่จะดำเนินการหลายข้อความ (Runnables) แล้วเขาควรใช้คลาส Looper ซึ่งรับผิดชอบในการสร้างคิวในหัวข้อ ตัวอย่างเช่นในขณะที่เขียนแอปพลิเคชันที่ดาวน์โหลดไฟล์จากอินเทอร์เน็ตเราสามารถใช้คลาส Looper เพื่อวางไฟล์ที่จะดาวน์โหลดในคิว

มันทำงานอย่างไร?

มีprepare()วิธีการเตรียม Looper จากนั้นคุณสามารถใช้loop()วิธีการสร้างวนรอบข้อความในเธรดปัจจุบันและตอนนี้ Looper ของคุณพร้อมที่จะดำเนินการตามคำขอในคิวจนกระทั่งคุณออกจากลูป

นี่คือรหัสที่คุณสามารถเตรียม Looper ได้

class LooperThread extends Thread {
      public Handler mHandler;

      @Override
      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              @Override
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
  }

17
AsyncTask ดีกว่าสำหรับวัตถุประสงค์นั้นและซับซ้อนน้อยกว่าเนื่องจากมันสรุปการจัดการเธรดทั้งหมด
Fernando Gallego

4
ควรมีการเพิ่มความคิดเห็น @Override ก่อนวิธีการ run () และ handleMessage ()
Andrew Mackenzie

5
เอกสารระบุว่าคุณต้องโทรหา looper.quit ในรหัสของคุณด้านบน Looper.loop จะบล็อกอย่างไม่มีกำหนด
AndroidDev

2
วิธีออกจากการวนซ้ำ ฉันหมายถึงสถานที่ที่จะรวม Looper.quit () ในตัวอย่างโค้ดข้างต้น?
Seenu69

6
ฉันคิดว่ามันจะดีกว่าถ้าใช้HandlerThreadซึ่งเป็นคลาสที่สะดวกสำหรับเธรดที่มี looper
Nimrod Dayan

287

คุณสามารถเข้าใจได้ดีขึ้นว่า Looper คืออะไรในบริบทของกรอบงาน GUI Looper สร้างขึ้นเพื่อทำ 2 สิ่ง

1) Looper แปลงเธรดปกติซึ่งสิ้นสุดเมื่อ run () เมธอดกลับเป็นบางสิ่งที่รันอย่างต่อเนื่องจนกว่าแอพ Android จะทำงานซึ่งจำเป็นต้องใช้ในกรอบ GUI (ในทางเทคนิคมันยังคงยุติลงเมื่อ run () วิธีการส่งคืน แต่ให้ฉัน ชี้แจงสิ่งที่ฉันหมายถึงในด้านล่าง)

2) Looper จัดคิวที่จะต้องจัดคิวงานซึ่งจำเป็นต้องใช้ใน GUI Framework ด้วย

อย่างที่คุณอาจทราบเมื่อเปิดตัวแอปพลิเคชันระบบจะสร้างเธรดของการดำเนินการสำหรับแอปพลิเคชันที่เรียกว่า "main" และแอปพลิเคชัน Android โดยปกติจะเรียกใช้บนเธรดเดี่ยวโดยค่าเริ่มต้น แต่หัวข้อหลักไม่ได้เป็นความลับบางอย่างด้ายพิเศษ มันเป็นเพียงเธรดปกติที่คล้ายกับเธรดที่คุณสร้างด้วยnew Thread()โค้ดซึ่งหมายความว่ามันจะยุติเมื่อเมธอด run () ส่งคืน! ลองนึกภาพตัวอย่างด้านล่าง

public class HelloRunnable implements Runnable {
    public void run() {
        System.out.println("Hello from a thread!");
    }

    public static void main(String args[]) {
        (new Thread(new HelloRunnable())).start();
    }
}

ทีนี้มาประยุกต์ใช้หลักการง่ายๆนี้กับแอพ Android จะเกิดอะไรขึ้นหากแอป Android ทำงานบนเธรดปกติ เธรดที่เรียกว่า "main" หรือ "UI" หรืออะไรก็ตามที่เริ่มต้นแอปพลิเคชันของคุณและดึง UI ทั้งหมด ดังนั้นหน้าจอแรกจะแสดงต่อผู้ใช้ แล้วตอนนี้ล่ะ? เธรดหลักยุติลง? ไม่ไม่ควร ควรรอจนกว่าผู้ใช้จะทำอะไรใช่ไหม แต่เราจะทำสิ่งนี้ให้สำเร็จได้อย่างไร? เราสามารถลองด้วยObject.wait()หรือThread.sleep(). ตัวอย่างเช่นเธรดหลักเสร็จสิ้นงานเริ่มต้นเพื่อแสดงหน้าจอแรกและพัก มันตื่นซึ่งหมายถึงการขัดจังหวะเมื่องานใหม่ที่จะทำคือดึง จนถึงตอนนี้ดีมาก แต่ในขณะนี้เราต้องการโครงสร้างข้อมูลที่คล้ายคิวเพื่อจัดงานหลายงาน นึกถึงกรณีที่ผู้ใช้สัมผัสหน้าจอเป็นลำดับและงานต้องใช้เวลานานกว่าจะเสร็จ ดังนั้นเราจำเป็นต้องมีโครงสร้างข้อมูลเพื่อเก็บงานที่ต้องทำในลักษณะเข้าก่อนออกก่อน นอกจากนี้คุณอาจจินตนาการว่าการใช้เธรดที่ทำงานอยู่ตลอดเวลาและกระบวนการทำงานเมื่อมาถึงโดยใช้อินเตอร์รัปต์นั้นไม่ใช่เรื่องง่ายและนำไปสู่โค้ดที่ซับซ้อน เราอยากจะสร้างกลไกใหม่เพื่อวัตถุประสงค์ดังกล่าวและนั่นคือสิ่งที่ Looper เป็นข้อมูลเกี่ยวกับ เอกสารอย่างเป็นทางการของการเรียน Looperกล่าวว่า "เธรดโดยค่าเริ่มต้นไม่มีการวนรอบข้อความที่เกี่ยวข้องกับพวกเขา" และ Looper เป็นคลาส "ที่ใช้ในการเรียกใช้การวนรอบข้อความสำหรับเธรด" ตอนนี้คุณสามารถเข้าใจความหมายของมัน

เพื่อให้สิ่งต่าง ๆ ชัดเจนยิ่งขึ้นลองตรวจสอบโค้ดที่เปลี่ยนเธรดหลัก ทุกอย่างที่เกิดขึ้นในระดับ ActivityThread ในเมธอด main () คุณสามารถค้นหาโค้ดด้านล่างซึ่งเปลี่ยนเธรดหลักปกติเป็นสิ่งที่เราต้องการ

public final class ActivityThread {
    ...
    public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();
        Looper.loop();
        ...
    }
}

และLooper.loop()method loop วนซ้ำและลบล้างข้อความและประมวลผลทีละรายการ:

public static void loop() {
    ...
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        ...
        msg.target.dispatchMessage(msg);
        ...
    }
}

ดังนั้นโดยทั่วไป Looper เป็นคลาสที่สร้างขึ้นเพื่อแก้ไขปัญหาที่เกิดขึ้นในกรอบงาน GUI แต่ความต้องการแบบนี้ก็สามารถเกิดขึ้นได้ในสถานการณ์อื่นเช่นกัน ที่จริงแล้วมันเป็นรูปแบบที่มีชื่อเสียงสำหรับแอพพลิเคชั่นแบบมัลติเธรดและคุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับมันได้ใน " การเขียนโปรแกรมพร้อมกันใน Java " โดย Doug Lea (โดยเฉพาะตอนที่ 4.1.4 "Worker Threads" นอกจากนี้คุณสามารถจินตนาการว่ากลไกแบบนี้ไม่เหมือนกันในกรอบของ Android แต่กรอบ GUI ทั้งหมดอาจต้องคล้ายกับสิ่งนี้ คุณสามารถพบกลไกเกือบเหมือนกันในกรอบงาน Java Swing


26
นี่เป็นคำตอบเดียวที่อธิบายทุกอย่างเกี่ยวกับสาเหตุที่ใช้คลาส Looper ฉันไม่แน่ใจว่าทำไมมันถึงไม่ใช่คำตอบอันดับต้น ๆ คำตอบอันดับสูงทั้งสามคำนี้ก็ไม่ได้อธิบายอะไรเลย
Andrew Koster

4
@AK นั่นเป็นเหตุผลที่ฉันเพิ่มคำตอบนี้แม้ดูเหมือนจะสายเกินไป ฉันดีใจที่คำตอบของฉันช่วยคุณได้! :)
김준호

1
@ Hey-men-whatsup ใช่
김준호

1
ก่อนที่จะอ่านสิ่งนี้ฉันเป็นเหมือน "Looper ???" และตอนนี้ "ใช่แล้วเราจะหารือกัน" ขอบคุณคนคำตอบที่ดี :)
umerk44

คำถามอย่างรวดเร็ว. คุณระบุว่าในเธรดหลักหลังจากที่ดึงองค์ประกอบ UI ทั้งหมดออกมาให้เข้าสู่โหมดสลีป แต่สมมติว่าผู้ใช้โต้ตอบกับปุ่มบนหน้าจอไม่ใช่ปุ่มคลิกที่ใส่ลงในคิวหลักจากนั้นวัตถุบางอย่างจะส่งไปยังกิจกรรมที่ถูกต้องจากนั้นเธรดหลักสำหรับกิจกรรมนั้นจะตื่นขึ้นมาและจะดำเนินการ รหัสสำหรับการโทรกลับสำหรับปุ่มนั้นคลิกใช่ไหม
จับภาพทรี

76

Looper ช่วยให้งานสามารถดำเนินการตามลำดับบนเธรดเดียว และตัวจัดการกำหนดงานเหล่านั้นที่เราต้องดำเนินการ มันเป็นสถานการณ์ทั่วไปที่ฉันพยายามจะอธิบายในตัวอย่างนี้:

class SampleLooper extends Thread {
@Override
public void run() {
  try {
    // preparing a looper on current thread     
    // the current thread is being detected implicitly
    Looper.prepare();

    // now, the handler will automatically bind to the
    // Looper that is attached to the current thread
    // You don't need to specify the Looper explicitly
    handler = new Handler();

    // After the following line the thread will start
    // running the message loop and will not normally
    // exit the loop unless a problem happens or you
    // quit() the looper (see below)
    Looper.loop();
  } catch (Throwable t) {
    Log.e(TAG, "halted due to an error", t);
  } 
}
}

ตอนนี้เราสามารถใช้ตัวจัดการในเธรดอื่น (พูด ui เธรด) เพื่อโพสต์งานบน Looper เพื่อดำเนินการ

handler.post(new Runnable()
{
public void run() {
//This will be executed on thread using Looper.
    }
});

ในเธรด UI เรามี Looper โดยปริยายซึ่งทำให้เราสามารถจัดการข้อความบนเธรด UI ได้


มันจะไม่ล็อคกระบวนการ UI ใด ๆ จริงหรือไม่?
gumuruh

4
ขอบคุณสำหรับการรวมตัวอย่างเกี่ยวกับวิธีการโพสต์ "งาน" ไปยังคิว
Peter Lillevold

นี่ไม่ได้อธิบายว่าทำไมคนเราถึงใช้คลาสนี้ได้อย่างไร
Andrew Koster

33

Android Looperเป็นเสื้อคลุมจะแนบMessageQueueไปThreadและจะจัดการการประมวลผลคิว มันดูเป็นความลับในเอกสาร Android และหลายครั้งที่เราอาจประสบLooperปัญหาการเข้าถึง UI ที่เกี่ยวข้อง หากเราไม่เข้าใจพื้นฐานมันก็ยากที่จะจัดการ

นี่คือบทความที่อธิบายLooperวงจรชีวิต, วิธีการใช้งานและการใช้งานLooperในHandler

ป้อนคำอธิบายรูปภาพที่นี่

Looper = Thread + MessageQueue


3
นี่ไม่ได้อธิบายว่าทำไมคนเราถึงใช้คลาสนี้ได้อย่างไร
Andrew Koster

14

คำจำกัดความของ Looper & Handler:

Looperเป็นคลาสที่เปลี่ยนเธรดให้เป็นPipeline Thread and Handlerช่วยให้คุณมีกลไกในการผลักงานจากเธรดอื่น ๆ

รายละเอียด:

ดังนั้นPipeLine Threadจึงเป็นเธรดที่สามารถรับงานเพิ่มเติมจากเธรดอื่นผ่าน Handler

Looperตั้งชื่อให้เพราะมันใช้วง - ใช้เวลางานต่อไปรันมันแล้วจะใช้เวลาหนึ่งต่อไปและอื่น ๆ ตัวจัดการเรียกว่าตัวจัดการเนื่องจากถูกใช้เพื่อจัดการหรือยอมรับภารกิจถัดไปในแต่ละครั้งจากเธรดอื่นและส่งผ่านไปยัง Looper (Thread หรือ PipeLine Thread)

ตัวอย่าง:

ตัวอย่างที่สมบูรณ์แบบของ Looper และ Handler หรือ PipeLine Thread คือการดาวน์โหลดภาพมากกว่าหนึ่งภาพหรืออัพโหลดไปยังเซิร์ฟเวอร์ (Http) ทีละภาพในเธรดเดี่ยวแทนที่จะเริ่มเธรดใหม่สำหรับการโทรแต่ละเครือข่ายในพื้นหลัง

อ่านเพิ่มเติมที่นี่เกี่ยวกับ Looper และ Handler และนิยามของ Pipeline Thread:

Android Guts: แนะนำ Loopers และ Handlers


7

Looperมีsynchronized MessageQueueที่ใช้ในการประมวลผลข้อความที่วางอยู่บนคิว

มันใช้Threadรูปแบบการจัดเก็บเฉพาะ

เพียงคนเดียวต่อLooper Threadวิธีการที่สำคัญ ได้แก่prepare(), และloop()quit()

prepare()เริ่มต้นปัจจุบันเป็นThread เป็นวิธีที่ใช้คลาสดังที่แสดงด้านล่างLooperprepare()staticThreadLocal

   public static void prepare(){
       ...
       sThreadLocal.set
       (new Looper());
   }
  1. prepare() ต้องถูกเรียกอย่างชัดเจนก่อนที่จะเรียกใช้เหตุการณ์ลูป
  2. loop()เรียกใช้ลูปเหตุการณ์ซึ่งรอให้ข้อความมาถึงข้อความของเธรดที่ระบุ เมื่อได้รับข้อความถัดไปแล้วloop()เมธอดจะกระจายข้อความไปยังตัวจัดการเป้าหมาย
  3. quit()ปิดลูปเหตุการณ์ มันไม่ได้จบการวนรอบ แต่มันจะ enqueues ข้อความพิเศษ

Looperสามารถตั้งโปรแกรมในThreadผ่านหลายขั้นตอน

  1. ต่ออายุ Thread

  2. เรียกLooper.prepare()เพื่อเริ่มต้นเธรดเป็นLooper

  3. สร้างหนึ่งรายการขึ้นHandlerไปเพื่อประมวลผลข้อความขาเข้า

  4. โทรไปยังข้อความกระบวนการจนกว่าวงจะบอกให้Looper.loop()quit()

5

อายุการใช้งานของ Java Threadสิ้นสุดลงหลังจากเสร็จสิ้นrun()วิธี ไม่สามารถเริ่มหัวข้อเดียวกันซ้ำได้

LooperแปลงปกติThreadเป็นวงวนข้อความ วิธีการที่สำคัญLooperคือ:

void prepare ()

เริ่มต้นเธรดปัจจุบันเป็น looper สิ่งนี้ให้โอกาสคุณในการสร้างตัวจัดการที่อ้างอิง looper นี้ก่อนเริ่มลูปจริง ๆ ต้องแน่ใจว่าได้เรียก loop () หลังจากเรียกใช้เมธอดนี้และสิ้นสุดโดยการเรียก exit ()

void loop ()

รันคิวข้อความในเธรดนี้ ให้แน่ใจว่าได้เรียกเลิก () เพื่อจบการวนซ้ำ

void quit()

ออกจาก looper

ทำให้เมธอด loop () สิ้นสุดลงโดยไม่ประมวลผลข้อความใด ๆ เพิ่มเติมในคิวข้อความ

นี้บทความ mindorksโดย Janishar อธิบายแนวคิดหลักในทางที่ดี

ป้อนคำอธิบายรูปภาพที่นี่

Looperเกี่ยวข้องกับเธรด หากคุณต้องการLooperในเธรด UI Looper.getMainLooper()จะส่งคืนเธรดที่เกี่ยวข้อง

คุณจำเป็นLooperจะต้องเกี่ยวข้องกับการจัดการ

Looper, HandlerและHandlerThreadเป็นวิธีของ Android ในการแก้ปัญหาการเขียนโปรแกรมแบบอะซิงโครนัส

เมื่อคุณมีHandlerคุณสามารถโทรด้านล่าง API

post (Runnable r)

ทำให้ Runnable r ถูกเพิ่มลงในคิวข้อความ runnable จะถูกรันบนเธรดที่ติดตั้ง handler นี้

boolean sendMessage (Message msg)

พุชข้อความไปยังจุดสิ้นสุดของคิวข้อความหลังข้อความที่ค้างอยู่ทั้งหมดก่อนเวลาปัจจุบัน จะได้รับใน handleMessage (ข้อความ) ในเธรดที่แนบมากับตัวจัดการนี้

HandlerThreadเป็นคลาสที่มีประโยชน์สำหรับการเริ่มหัวข้อใหม่ที่มี looper Looper สามารถใช้สร้างคลาสตัวจัดการได้

ในบางสถานการณ์คุณไม่สามารถเรียกใช้Runnableงานบน UI Thread ได้ เช่นการทำงานของเครือข่าย: ส่งข้อความบนซ็อกเก็ตเปิด URL และรับเนื้อหาโดยการอ่านInputStream

ในกรณีเหล่านี้HandlerThreadมีประโยชน์ คุณสามารถรับLooperวัตถุจากHandlerThreadและสร้างHandlerบนHandlerThreadแทนเธรดหลัก

HandlerThreadรหัสจะเป็นเช่นนี้

@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

อ้างถึงโพสต์ด้านล่างสำหรับตัวอย่างรหัส:

Android: ขนมปังปิ้งในหัวข้อ


5

ทำความเข้าใจกับหัวข้อ Looper

java เธรดหน่วยการเรียกใช้งานซึ่งออกแบบมาเพื่อทำงานในวิธีการ run () & สิ้นสุดหลังจากนั้น: ป้อนคำอธิบายรูปภาพที่นี่

แต่ใน Android มีกรณีการใช้งานมากมายที่เราต้องทำให้ Thread ยังมีชีวิตอยู่และรอการป้อนข้อมูล / เหตุการณ์ของผู้ใช้เช่น หัวข้อ UI Main Threadaka

เธรดหลักใน Android เป็นเธรด Java ที่เริ่มต้นโดย JVM เมื่อเปิดตัวแอพและยังคงทำงานต่อไปจนกว่าผู้ใช้เลือกที่จะปิดหรือพบข้อยกเว้นที่ไม่สามารถจัดการได้

เมื่อเปิดใช้งานแอปพลิเคชันระบบจะสร้างเธรดของการดำเนินการสำหรับแอปพลิเคชันที่เรียกว่า "main" เธรดนี้มีความสำคัญมากเนื่องจากเป็นหน้าที่ของการจัดส่งเหตุการณ์ไปยังวิดเจ็ตส่วนต่อประสานผู้ใช้ที่เหมาะสมรวมถึงกิจกรรมการวาด

ป้อนคำอธิบายรูปภาพที่นี่

ตอนนี้ชี้ให้เห็นที่นี่คือแม้ว่าเธรดหลักคือเธรด Java แต่ก็ยังคงฟังเหตุการณ์ของผู้ใช้และวาด 60 เฟรมต่อวินาทีเฟรมบนหน้าจอและยังคงไม่ตายหลังจากแต่ละรอบ มันเป็นอย่างไร

คำตอบคือ Looper Class : Looper เป็นคลาสที่ใช้เพื่อให้เธรดมีชีวิตอยู่และจัดการคิวข้อความเพื่อดำเนินงานในเธรดนั้น

เธรดโดยค่าเริ่มต้นไม่มีการวนรอบข้อความที่เกี่ยวข้องกับพวกเขา แต่คุณสามารถกำหนดหนึ่งโดยการเรียก Looper.prepare () ในวิธีการเรียกใช้แล้วเรียก Looper.loop ()

วัตถุประสงค์ของ Looper คือการทำให้เธรดมีชีวิตอยู่และรอรอบถัดไปของMessageวัตถุอินพุตเพื่อทำการคำนวณซึ่งมิฉะนั้นจะถูกทำลายหลังจากรอบแรกของการดำเนินการ

หากคุณต้องการขุดลึกลงไปถึงวิธี Looper จัดการMessageวัตถุคิวแล้วคุณสามารถดูรหัสที่มาของLooperclass:

https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/java/android/os/Looper.java

ด้านล่างเป็นตัวอย่างของวิธีการสร้างLooper Threadและสื่อสารกับActivityคลาสโดยใช้LocalBroadcast

class LooperThread : Thread() {

    // sendMessage success result on UI
    private fun sendServerResult(result: String) {
        val resultIntent = Intent(ServerService.ACTION)
        resultIntent.putExtra(ServerService.RESULT_CODE, Activity.RESULT_OK)
        resultIntent.putExtra(ServerService.RESULT_VALUE, result)
        LocalBroadcastManager.getInstance(AppController.getAppController()).sendBroadcast(resultIntent)
    }

    override fun run() {
        val looperIsNotPreparedInCurrentThread = Looper.myLooper() == null

        // Prepare Looper if not already prepared
        if (looperIsNotPreparedInCurrentThread) {
            Looper.prepare()
        }

        // Create a handler to handle messaged from Activity
        handler = Handler(Handler.Callback { message ->
            // Messages sent to Looper thread will be visible here
            Log.e(TAG, "Received Message" + message.data.toString())

            //message from Activity
            val result = message.data.getString(MainActivity.BUNDLE_KEY)

            // Send Result Back to activity
            sendServerResult(result)
            true
        })

        // Keep on looping till new messages arrive
        if (looperIsNotPreparedInCurrentThread) {
            Looper.loop()
        }
    }

    //Create and send a new  message to looper
    fun sendMessage(messageToSend: String) {
        //Create and post a new message to handler
        handler!!.sendMessage(createMessage(messageToSend))
    }


    // Bundle Data in message object
    private fun createMessage(messageToSend: String): Message {
        val message = Message()
        val bundle = Bundle()
        bundle.putString(MainActivity.BUNDLE_KEY, messageToSend)
        message.data = bundle
        return message
    }

    companion object {
        var handler: Handler? = null // in Android Handler should be static or leaks might occur
        private val TAG = javaClass.simpleName

    }
}

การใช้งาน :

 class MainActivity : AppCompatActivity() {

    private var looperThread: LooperThread? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // start looper thread
        startLooperThread()

        // Send messages to Looper Thread
        sendMessage.setOnClickListener {

            // send random messages to looper thread
            val messageToSend = "" + Math.random()

            // post message
            looperThread!!.sendMessage(messageToSend)

        }   
    }

    override fun onResume() {
        super.onResume()

        //Register to Server Service callback
        val filterServer = IntentFilter(ServerService.ACTION)
        LocalBroadcastManager.getInstance(this).registerReceiver(serverReceiver, filterServer)

    }

    override fun onPause() {
        super.onPause()

        //Stop Server service callbacks
     LocalBroadcastManager.getInstance(this).unregisterReceiver(serverReceiver)
    }


    // Define the callback for what to do when data is received
    private val serverReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            val resultCode = intent.getIntExtra(ServerService.RESULT_CODE, Activity.RESULT_CANCELED)
            if (resultCode == Activity.RESULT_OK) {
                val resultValue = intent.getStringExtra(ServerService.RESULT_VALUE)
                Log.e(MainActivity.TAG, "Server result : $resultValue")

                serverOutput.text =
                        (serverOutput.text.toString()
                                + "\n"
                                + "Received : " + resultValue)

                serverScrollView.post( { serverScrollView.fullScroll(View.FOCUS_DOWN) })
            }
        }
    }

    private fun startLooperThread() {

        // create and start a new LooperThread
        looperThread = LooperThread()
        looperThread!!.name = "Main Looper Thread"
        looperThread!!.start()

    }

    companion object {
        val BUNDLE_KEY = "handlerMsgBundle"
        private val TAG = javaClass.simpleName
    }
}

เราสามารถใช้งาน Async หรือบริการ Intent แทนได้หรือไม่?

  • งาน Async ถูกออกแบบมาเพื่อดำเนินการสั้น ๆ ในพื้นหลังและให้โปรแกรมและผลลัพธ์บนเธรด UI งาน Async มีข้อ จำกัด เช่นคุณไม่สามารถสร้างมากกว่า128 งาน AsyncและThreadPoolExecutorจะช่วยให้เพียงไม่เกิน 5 งาน

  • IntentServicesนอกจากนี้ยังได้รับการออกแบบที่จะทำงานพื้นหลังสำหรับระยะเวลาเล็ก ๆ น้อย ๆ อีกต่อไปและคุณสามารถใช้ในการสื่อสารกับLocalBroadcast Activityแต่บริการจะถูกทำลายหลังจากการปฏิบัติภารกิจ while(true){...}หากคุณต้องการให้มันทำงานเป็นเวลานานกว่าที่คุณต้องทำเช่น hecks

กรณีการใช้งานที่มีความหมายอื่น ๆ สำหรับ Looper Thread:

  • ใช้สำหรับการสื่อสารซ็อกเก็ต 2 ทางที่เซิร์ฟเวอร์คอยรับฟังไคลเอ็นต์ซ็อกเก็ตและรับทราบการตอบกลับ

  • การประมวลผลบิตแมปในพื้นหลัง ส่ง URL รูปภาพไปยังเธรด Looper และจะใช้เอฟเฟ็กต์ฟิลเตอร์และเก็บไว้ในตำแหน่งเทมพีโรรี่แล้วจึงถ่ายทอดเส้นทางชั่วคราวของรูปภาพ


4

คำตอบนี้ไม่มีอะไรเกี่ยวข้องกับคำถาม แต่การใช้ Looper และวิธีที่ผู้คนสร้างตัวจัดการและ Looper ในคำตอบทั้งหมดที่นี่เป็นวิธีปฏิบัติที่ไม่ดี (ฉันมีคำอธิบายบางอย่างที่ถูกต้อง) ฉันต้องโพสต์สิ่งนี้:

HandlerThread thread = new HandlerThread(threadName);
thread.start();
Looper looper = thread.getLooper();
Handler myHandler = new Handler(looper);

และสำหรับการใช้งานอย่างเต็มรูปแบบ


3

การจัดการหลายรายการหรืออัปโหลดรายการในบริการเป็นตัวอย่างที่ดีกว่า

HandlerและAsnycTaskมักใช้เพื่อเผยแพร่กิจกรรม / ข้อความระหว่างUI (เธรด) และเธรดผู้ปฏิบัติงานหรือเพื่อชะลอการดำเนินการ ดังนั้นพวกเขาจึงเกี่ยวข้องกับ UI มากขึ้น

Looperจับงาน ( Runnables, ฟิวเจอร์ส ) ในคิวที่เกี่ยวข้องกับหัวข้อในพื้นหลัง - แม้จะมีการโต้ตอบกับผู้ใช้หรือไม่ที่แสดง UI (ดาวน์โหลดแอปไฟล์ในพื้นหลังระหว่างการโทรก)


1

Looper คืออะไร

จาก DOCS

Looper

Looperthreadชั้นที่ใช้ในการเรียกใช้ข้อความห่วงหา เธรดโดยค่าเริ่มต้นไม่มีการวนรอบข้อความที่เกี่ยวข้อง เมื่อต้องการสร้างให้เรียกprepare()ในเธรดที่จะเรียกใช้การวนซ้ำและจากนั้นloop()ให้ประมวลผลข้อความจนกว่าการวนซ้ำจะหยุด

  • A Looperคือลูปการจัดการข้อความ:
  • ลักษณะสำคัญของ Looper คือมันเกี่ยวข้องกับเธรดที่สร้าง Looper
  • คลาส Looper รักษาMessageQueueซึ่งมีข้อความรายการ ลักษณะสำคัญของ Looper คือมันเกี่ยวข้องกับเธรดที่สร้าง Looper
  • Looperตั้งชื่อให้เพราะมันใช้วง - ใช้เวลางานต่อไปรันมันแล้วจะใช้เวลาหนึ่งต่อไปและอื่น ๆ สิ่งHandlerนี้เรียกว่าตัวจัดการเนื่องจากบางคนไม่สามารถประดิษฐ์ชื่อที่ดีกว่าได้
  • Android Looperเป็นคลาส Java ภายในอินเทอร์เฟซผู้ใช้ Android ที่ร่วมกับคลาสตัวจัดการเพื่อประมวลผลเหตุการณ์ UI เช่นการคลิกปุ่มการวาดหน้าจอใหม่และสวิตช์การวางแนว

มันทำงานอย่างไร?

ป้อนคำอธิบายรูปภาพที่นี่

การสร้าง Looper

เธรดได้รับLooperและMessageQueueโดยการเรียกLooper.prepare()หลังจากการทำงาน Looper.prepare()ระบุเธรดการโทร, สร้าง Looper และMessageQueueวัตถุและเชื่อมโยงเธรด

รหัสตัวอย่าง

class MyLooperThread extends Thread {

      public Handler mHandler; 

      public void run() { 

          // preparing a looper on current thread  
          Looper.prepare();

          mHandler = new Handler() { 
              public void handleMessage(Message msg) { 
                 // process incoming messages here
                 // this will run in non-ui/background thread
              } 
          }; 

          Looper.loop();
      } 
  }

สำหรับข้อมูลเพิ่มเติมตรวจสอบด้านล่างโพสต์


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