วิธีการโพสต์ทำอะไรกันแน่?


106

ฉันได้พบกับคุณสมบัติที่แปลกมาก

เมื่อฉันพยายามเรียกใช้ภาพเคลื่อนไหวบนเธรดหลักมันไม่เริ่มทำงาน เมื่อฉันเรียกใช้ภาพเคลื่อนไหวโดยใช้ไฟล์

getView().post(new Runnable() {
            @Override
            public void run() {
                getView().startAnimation(a);
            }
        });

มันจะเริ่มต้น

ผมเคยพิมพ์ก่อนที่จะเริ่มการเคลื่อนไหวและการพิมพ์ทั้งสองCurrentThreadmain

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

แก้ไข: ให้ฉันเคลียร์สิ่งต่างๆ - คำถามของฉันคือทำไมการเริ่มแอนิเมชั่นในโพสต์จึงทำให้มันเริ่มต้นเมื่อเริ่มแอนิเมชั่นบนเธรดหลักไม่ได้


พฤติกรรมนี้เฉพาะกับเวอร์ชัน Android หรือไม่ ฉันไม่สามารถทำซ้ำบน Android 4.1.2!
Akdeniz

ฉันสร้างพฤติกรรมนี้ซ้ำบน Android 2.3.3 แต่สำหรับAnimationDrawable! Animationอินสแตนซ์ทั่วไปเริ่มเคลื่อนไหวได้สำเร็จในแต่ละการตั้งค่า ในAnimationDrawableกรณี; เมื่อคุณพยายามเริ่มต้นมันonCreateจะไม่เริ่มต้นเนื่องจากไม่ได้ติดอยู่กับมุมมองในขณะนั้น AnimationDrawableดังนั้นจึงไม่เป็นปัญหาสำหรับเกลียว อาจจะเหมือนกันสำหรับAnimation? developer.android.com/guide/topics/graphics/…
Akdeniz

คำตอบ:


161

post : post ทำให้ Runnable ถูกเพิ่มลงในคิวข้อความ

Runnable:แสดงถึงคำสั่งที่สามารถเรียกใช้งานได้ มักใช้เพื่อรันโค้ดในเธรดอื่น

run () : เริ่มเรียกใช้ส่วนที่ใช้งานอยู่ของรหัสคลาส เมธอดนี้เรียกว่าเมื่อเธรดเริ่มต้นที่ถูกสร้างขึ้นด้วยคลาสที่ใช้ Runnable

getView().post(new Runnable() {

         @Override
         public void run() {
             getView().startAnimation(a);
         }
     });

รหัส :getView().startAnimation(a);

ในรหัสของคุณ

โพสต์ทำให้ Runnable ( รหัสจะถูกเรียกใช้ในเธรดอื่น) เพื่อเพิ่มคิวข้อความ

ดังนั้น startAnimation จะเริ่มทำงานในเธรดใหม่เมื่อดึงข้อมูลจากmessageQueue

[แก้ไข 1]

เหตุใดเราจึงใช้เธรดใหม่แทนเธรด UI (เธรดหลัก)

เธรด UI:

  • เมื่อแอปพลิเคชันเริ่มต้น Ui Thread จะถูกสร้างขึ้นโดยอัตโนมัติ

  • มีหน้าที่จัดส่งเหตุการณ์ไปยังวิดเจ็ตที่เหมาะสมและรวมถึงเหตุการณ์การวาดด้วย

  • นอกจากนี้ยังเป็นเธรดที่คุณโต้ตอบกับวิดเจ็ต Android ด้วย

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

จะเกิดอะไรขึ้นหากผู้ใช้กดปุ่มซึ่งจะใช้เวลานาน

((Button)findViewById(R.id.Button1)).setOnClickListener(           
             new OnClickListener() {        
        @Override
    public void onClick(View v) {
            final Bitmap b = loadImageFromNetwork();
            mImageView.setImageBitmap(b);
}
});

UI หยุดทำงาน โปรแกรมอาจผิดพลาดด้วยซ้ำ

public void onClick(View v) {
  new Thread(new Runnable() {
    public void run() {
        final Bitmap b = loadImageFromNetwork();
        mImageView.setImageBitmap(b);
    }
  }).start();
}

มันทำลายกฎของ Android ที่ไม่เคยอัปเดต UI โดยตรงจากเธรดของผู้ปฏิบัติงาน

Android มีหลายวิธีในการเข้าถึงเธรด UI จากเธรดอื่น

  • Activity.runOnUiThread (รันได้)
  • View.post (รันได้)
  • View.postDelayed (รันได้ยาว)
  • ตัวจัดการ

เช่นด้านล่าง

View.post (รันได้)

public void onClick(View v) {
  new Thread(new Runnable() {
    public void run() {
      final Bitmap b = loadImageFromNetwork();
      mImageView.post(new Runnable() {
        public void run() {
          mImageView.setImageBitmap(b);
        }
      });
    }
  }).start();
}

ตัวจัดการ

final Handler myHandler = new Handler(Looper.getMainLooper());

(new Thread(new Runnable() {

    @Override
    public void run() {
       final Bitmap b = loadImageFromNetwork();
      myHandler.post(new Runnable() {                           

        @Override
        public void run() {
           mImageView.setImageBitmap(b);
          }
        });
      }
    })).start();                
}

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

สำหรับข้อมูลเพิ่มเติม

http://android-developers.blogspot.com/2009/05/painless-threading.html

http://www.aviyehuda.com/blog/2010/12/20/android-multithreading-in-a-ui-environment/


15
เหตุใดการเริ่มแอนิเมชั่นในโพสต์จึงแตกต่างจากการรันบนเธรดหลักเมื่อทั้งคู่ทำงานบนเธรดเดียวกันในที่สุด
Gal

เนื่องจากรูปแบบเธรดเดียวนี้สามารถให้ประสิทธิภาพที่ไม่ดีในแอปพลิเคชัน Android
Talha

1
ประสิทธิภาพที่ไม่ดีเกี่ยวข้องกับการไม่แสดงภาพเคลื่อนไหวอย่างไร
Gal

17
ฉันไม่คิดว่าสิ่งนี้จะตอบคำถามนี้เป็นเหมือนคำตอบทั่วไปสำหรับผู้เริ่มต้นที่ไม่รู้อะไรเกี่ยวกับ ui-thread และ multi threading นี่ไม่ได้อธิบายว่าทำไมการโยนภาพเคลื่อนไหวไปข้างหน้าในคิวจึงทำให้ภาพเคลื่อนไหวทำงานได้ ภาพเคลื่อนไหวควรเป็นสิ่งที่จะดำเนินการโดยตรงใน ui-thread โดยไม่ต้องใช้เทคนิค post () หรือ runOnUiThread ()
carrizo

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

35

กำลังดำเนินการบน onCreate หรือ onCreateView? ในกรณีนี้แอปอาจไม่อยู่ในสถานะที่แนบ View เข้ากับหน้าต่าง อัลกอริทึมจำนวนมากตามเมตริกมุมมองอาจไม่ทำงานเนื่องจากอาจไม่ได้คำนวณสิ่งต่างๆเช่นการวัดและตำแหน่งของ View โดยทั่วไปแล้วแอนิเมชั่น Android ต้องการให้พวกเขาทำงานผ่านทางคณิตศาสตร์ UI

View.post จะจัดคิวภาพเคลื่อนไหวบนลูปข้อความของ View ดังนั้นเมื่อมุมมองถูกแนบเข้ากับหน้าต่างระบบจะเรียกใช้ภาพเคลื่อนไหวแทนการดำเนินการด้วยตนเอง

คุณกำลังเรียกใช้สิ่งต่างๆบนเธรด UI แต่ในเวลาอื่น


คำตอบที่ยอมรับนั้นทำให้เข้าใจผิดโดยที่ผู้โพสต์ระบุว่า "โพสต์ทำให้เกิด Runnable (โค้ดจะถูกเรียกใช้ในเธรดอื่น)" นี่คือคำตอบที่ถูกต้อง "คุณกำลังเรียกใช้สิ่งต่างๆบนเธรด UI แต่ในเวลาอื่น" - บวก 1
smitty1

18

ได้ดูที่นี่สำหรับคำตอบที่ดี view.post () เหมือนกับ handler.post () สวยมาก จะเข้าสู่คิวเธรดหลักและดำเนินการหลังจากงานอื่น ๆ ที่รอดำเนินการเสร็จสิ้น หากคุณเรียก activity.runOnUiThread () ระบบจะเรียกใช้เธรด UI ทันที


31
ความแตกต่างอย่างมาก (และเป็นประโยชน์อย่างยิ่ง) อย่างหนึ่งที่ฉันพบคือ runnable ใน view.post () จะถูกเรียกเมื่อ View แสดงครั้งแรก IE คุณสามารถตั้งค่าให้เริ่มแอนิเมชั่นตามอัตราเงินเฟ้อของมุมมองจากนั้นในอนาคตในที่สุดก็เพิ่มลงในลำดับชั้นของมุมมอง เมื่อถึงจุดนั้นภาพเคลื่อนไหวจะทำงานและคุณไม่ต้องกังวลกับมัน
DeeV

อันที่จริง handler.post () ไม่ได้โพสต์ข้อความ / runnable บนเธรดหลักเสมอไป ขึ้นอยู่กับวิธีสร้างตัวจัดการ (สามารถเชื่อมโยงกับ Looper บนเธรดอื่นได้) ในทางกลับกัน view.post () จะทำงานบนเธรดหลักเสมอ
Yair Kukielka

4

ปัญหาที่ฉันคิดว่าอาจเป็นวิธีวงจรชีวิตที่คุณเรียกใช้เมธอด post () คุณทำใน onCreate () หรือไม่? หากเป็นเช่นนั้นดูสิ่งที่ฉันพบในเอกสาร onResume () ของกิจกรรม:

onResume ()

เพิ่มใน API ระดับ 1 เป็นโมฆะ onResume () เรียกหลังจาก onRestoreInstanceState (Bundle), onRestart () หรือ onPause () เพื่อให้กิจกรรมของคุณเริ่มโต้ตอบกับผู้ใช้ นี่เป็นจุดเริ่มต้นที่ดีในการเริ่มต้นแอนิเมชั่นเปิดอุปกรณ์พิเศษสำหรับการเข้าถึง (เช่นกล้องถ่ายรูป) เป็นต้น

https://developer.android.com/reference/android/app/Activity.html#onResume ()

ดังที่ Joe Plante กล่าวว่ามุมมองอาจไม่พร้อมที่จะเริ่มภาพเคลื่อนไหวในขณะที่คุณเรียกโพสต์ () ดังนั้นลองย้ายไปที่ onResume ()

PD: จริงๆแล้วถ้าคุณย้ายโค้ดไปที่ onResume () ฉันคิดว่าคุณสามารถลบการเรียกโพสต์ () ได้เนื่องจากคุณอยู่ใน ui-thread แล้วและมุมมองควรพร้อมที่จะเริ่มภาพเคลื่อนไหว


3
onResumeอาจเรียกได้หลายครั้ง (หน้าจอเข้าสู่โหมดสลีปกิจกรรมถูกผลักไปที่แบ็คสแต็ก ฯลฯ ... ) หลังจากเริ่มต้นเมื่อ "มุมมองพร้อม" หากถูกเรียกจากonResumeนั้นอาจจำเป็นต้องใช้ธงเพื่อติดตามสภาพอากาศที่แอนิเมชั่นได้เริ่มต้นไปแล้วเพื่อหลีกเลี่ยง (ซ้ำ) เริ่มหลายครั้ง
samis
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.