BroadcastReceiver.onReceive ทำงานในเธรด UI เสมอหรือไม่


117

ใน app ของฉันฉันสร้างที่กำหนดเองและลงทะเบียนกับบริบทของฉันด้วยตนเองผ่านทางBroadcastReceiver Context.registerReceiverฉันยังมีAsyncTaskที่ยื้อแจ้ง-Intent Context.sendBroadcastของผ่าน Intent ถูกส่งมาจากเธรดผู้ปฏิบัติงานที่ไม่ใช่ UI แต่ดูเหมือนว่าBroadcastReceiver.onReceive(ซึ่งได้รับ Intents ดังกล่าว) จะทำงานในเธรด UI เสมอ (ซึ่งดีสำหรับฉัน) สิ่งนี้รับประกันหรือไม่ควรพึ่งพาสิ่งนั้น?

คำตอบ:


163

BroadcastReceiver.onReceive ทำงานในเธรด UI เสมอหรือไม่

ใช่.


9
เอกสารนี้อยู่ที่ไหน
Hannes Struß

15
@hannes: 99.44% ของเวลาที่ Android เรียกรหัสของคุณมันจะอยู่ในเธรดแอปพลิเคชันหลัก วิธีวงจรชีวิตทั้งหมด (เช่นonCreate(), onReceive()) จะเรียกว่าในหัวข้อการประยุกต์ใช้หลัก และมีการบันทึกไว้ในเอกสารสำหรับonReceive(): goo.gl/8kPuH
CommonsWare

2
โอเคฉันแค่ตีความ "ปกติจะเรียกในเธรดหลัก" จากเอกสารว่า "เสมอ" และหวังว่าสิ่งต่างๆจะไม่พัง ;-) ขอบคุณ!
Hannes Struß

4
@Hannes Struß: ฉันไม่รู้ว่าทำไมพวกเขาถึงป้องกันความเสี่ยงภาษาของพวกเขาด้วย "ปกติ" ฉันไม่สามารถนึกถึงกรณีใด ๆ ที่onReceive()ถูกเรียกบนเธรดนอกเหนือจากเธรดแอปพลิเคชันหลัก ("UI")
CommonsWare

31
@CommonsWare: "ฉันไม่สามารถนึกถึงกรณีใด ๆ ที่ onReceive () ถูกเรียกบนเธรดอื่นนอกเหนือจากเธรดแอปพลิเคชันหลัก (" UI ")" - ในกรณีนี้คือถ้า BroadcastReceiver ลงทะเบียนโดยใช้ registerReceiver (BroadcastReceiver, IntentFilter, String, Handler) อาร์กิวเมนต์ตัวจัดการไม่เป็นโมฆะและอ้างถึงตัวจัดการที่สร้างขึ้นในเธรดอื่นที่ไม่ใช่เธรดแอ็พพลิเคชันหลัก
Jules

76

เนื่องจากคุณลงทะเบียนเครื่องรับแบบไดนามิกคุณสามารถระบุว่าเธรดอื่น (นอกเหนือจากเธรด UI) จัดการกับไฟล์onReceive(). นี้จะกระทำผ่านพารามิเตอร์ Handler ของ registerReceiver ()

ที่กล่าวว่าหากคุณไม่ได้ระบุตัวจัดการอื่นมันจะได้รับการจัดการบนเธรด UI เสมอ


ใช่. ดูเหมือนว่าความสามารถของคุณในการเปลี่ยนแปลงผ่านพารามิเตอร์ Handler คือสาเหตุที่พวกเขา "ป้องกัน" ภาษาของตนในเอกสาร
Andrew Mackenzie

64

BroadcastReceiver.onReceive ทำงานในเธรด UI เสมอหรือไม่

โดยปกติแล้วทุกอย่างขึ้นอยู่กับวิธีที่คุณลงทะเบียน

หากคุณลงทะเบียนBroadcastReceiverโดยใช้:

registerReceiver(BroadcastReceiver receiver, IntentFilter filter)

มันจะทำงานในหัวข้อกิจกรรมหลัก (aka หัวข้อ UI)

หากคุณลงทะเบียนBroadcastReceiverโดยใช้การHandler รันที่ถูกต้องบนเธรดอื่น :

registerReceiver (BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler)

มันจะทำงานในบริบทของไฟล์ Handler

ตัวอย่างเช่น:

HandlerThread handlerThread = new HandlerThread("ht");
handlerThread.start();
Looper looper = handlerThread.getLooper();
Handler handler = new Handler(looper);
context.registerReceiver(receiver, filter, null, handler); // Will not run on main thread

รายละเอียดที่นี่และที่นี่


3
หลังจากดูตัวเลือกนี้มาระยะหนึ่งในที่สุดฉันก็รู้ว่า LocalBroadcastManager ไม่รองรับการใช้ตัวจัดการแบบกำหนดเอง ดังนั้นหากคุณใช้ LBM แทนบริบทในการลงทะเบียนผู้รับของคุณวิธีนี้ใช้ไม่ได้ น่าเสียดายที่ในกรณีนี้ดูเหมือนว่าตัวเลือกเดียวของเราที่เหลือคือการใช้บริการเพื่อทำงานเบื้องหลังและหลีกเลี่ยง ANR ที่เครื่องรับจะทำงานหลังจากไม่มีการใช้งาน 10 วินาที
gMale

9

เนื่องจากคำตอบก่อนหน้านี้ระบุไว้อย่างถูกต้องonReceiveจะทำงานบนเธรดซึ่งลงทะเบียนด้วยหากรสชาติของ registerReceiver()สิ่งนั้นยอมรับตัวจัดการถูกเรียก - มิฉะนั้นในเธรดหลัก

ยกเว้นในกรณีที่เครื่องรับลงทะเบียนกับLocalBroadcastManagerและออกอากาศผ่าน sendBroadcastSync- ซึ่งดูเหมือนว่าจะทำงานบนเธรดที่โทรsendBroadcastSync.


ฉันไม่เห็นด้วยกับส่วนand the broadcast is via sendBroadcastSyncนี้ เมื่อเราใช้LocalBroadcastManagerเพื่อลงทะเบียนผู้รับจะต้องถูกเรียกโดยเธรดหลักว่าใช้sendBroadcastSyncหรือsendBroadcast. ดังนั้นกุญแจสำคัญคือการใช้LocalBroadcastManagerเพื่อลงทะเบียน ฉันถูกไหม?
kidoher

@kidoher: คุณทำตามลิงก์รหัสที่นี่: stackoverflow.com/q/20820244/281545หรือไม่
Mr_and_Mrs_D

0

มี Context.registerReceiver (ตัวรับ BroadcastReceiver, ตัวกรอง IntentFilter, String broadcastPermission, Handler Scheduler)

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