การเข้าถึงตัวจัดการเธรด UI จากบริการ


90

ฉันกำลังลองสิ่งใหม่ ๆ บน Android ซึ่งฉันจำเป็นต้องเข้าถึงตัวจัดการเธรด UI

ฉันรู้สิ่งต่อไปนี้:

  1. เธรด UI มีตัวจัดการและตัววนรอบของตัวเอง
  2. ข้อความใด ๆ จะถูกใส่ลงในคิวข้อความของเธรด UI
  3. ลูปเกอร์รับเหตุการณ์และส่งต่อไปยังตัวจัดการ
  4. ตัวจัดการจะจัดการข้อความและส่งเหตุการณ์เฉพาะไปยัง UI

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

ฉันต้องการทราบว่าเป็นไปได้หรือไม่ ถ้าเป็นเช่นนั้นโปรดแนะนำข้อมูลโค้ดเพื่อที่ฉันจะได้ลองใช้

ขอแสดงความนับถือ Girish

คำตอบ:


179

ข้อมูลโค้ดนี้สร้างตัวจัดการที่เชื่อมโยงกับเธรดหลัก (UI):

Handler handler = new Handler(Looper.getMainLooper());

จากนั้นคุณสามารถโพสต์สิ่งต่างๆเพื่อดำเนินการในเธรดหลัก (UI) ดังนี้:

handler.post(runnable_to_call_from_main_thread);

หากตัวจัดการถูกสร้างขึ้นจากเธรดหลัก (UI) อาร์กิวเมนต์สามารถละเว้นได้เพื่อความกะทัดรัด:

Handler handler = new Handler();

Android Dev คู่มือเกี่ยวกับกระบวนการและหัวข้อมีข้อมูลเพิ่มเติม


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

1
ยอดเยี่ยม. ใช้งานได้อย่างมีเสน่ห์และมีประโยชน์มาก ขอขอบคุณ.
JRun

สมบูรณ์แบบ ^^ เพิ่งใช้เพื่ออัปเดต UI ของฉันจาก StreamingService สิ่งที่ฉันต้องการขอบคุณ!
An-droid

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

ฉันเดาว่าเราจะไม่มีทางรู้
Denny

28

สร้างMessengerวัตถุที่แนบมากับคุณHandlerและส่งMessengerต่อไปยังService(เช่นในส่วนIntentพิเศษสำหรับstartService()) Serviceแล้วสามารถส่งMessageไปผ่านทางHandler นี่คือตัวอย่างแอปพลิเคชันที่สาธิตสิ่งนี้Messenger


ขอบคุณสำหรับเคล็ดลับนี้ นี่เป็นประโยชน์ โปรดดูสแต็กต่อไปนี้สำหรับโฟลว์เหตุการณ์สัมผัสไปยังกิจกรรมของฉัน MyDemo.dispatchTouchEvent (MotionEvent) บรรทัด: 20 PhoneWindow $ DecorView.dispatchTouchEvent (MotionEvent) บรรทัด: 1696 ViewRoot.handleMessage (ข้อความ) บรรทัด: 1658 ViewRoot (Handler) .dispatchMessage (ข้อความ ) line: 99 Looper.loop () line: 123 // การจัดการเหตุการณ์เริ่มต้นที่นี่ ActivityThread.main (String []) line: 4203 ที่นี่ ViewRoot เป็น Handler ฉันต้องการรับข้อมูลอ้างอิงของตัวจัดการนี้ ... เป็นไปได้ไหมที่จะได้รับสิ่งนี้จากแอปพลิเคชันของฉัน
iLikeAndroid

@iLikeAndroid: หากคุณไม่ได้สร้างHandlerคุณจะไม่สามารถเข้าถึงได้ AFAIK
CommonsWare

ขอขอบคุณ. ฉันได้พยายามสร้างอินสแตนซ์ของ ViewRoot นี่ไม่ใช่อะไรนอกจากตัวจัดการ ตอนนี้ฉันสามารถออกข้อความในเครื่องจัดการนี้ได้แล้ว ตัวจัดการกำลังรับข้อความ แต่ ViewRoot ไม่สามารถประมวลผลข้อความได้เนื่องจากไม่ได้เริ่มต้นอย่างถูกต้อง ฉันต้องการเรียก ViewRoot.setView () เพื่อเริ่มต้นข้อมูลที่เหมาะสมกับ ViewRoot ฉันต้องการทราบว่ามีมุมมองเริ่มต้นหรือมุมมองพื้นฐาน ฯลฯ ที่ฉันสามารถใช้เพื่อเริ่มต้นได้หรือไม่?
iLikeAndroid

@iLikeAndroid: ไม่มีViewRootใน Android SDK, AFAICT
CommonsWare

1
@ hadez30: โดยส่วนตัวแล้วฉันไม่ได้ใช้บริการแบบผูกมัดมากนัก คุณยังคงสามารถใช้ a Handler/ Messengerได้แม้ว่าฉันจะแทนที่สิ่งนั้นทั้งหมดด้วยบัสเหตุการณ์ (เช่น EventBus ของ greenrobot)
CommonsWare

4

ในขณะนี้ฉันชอบใช้ไลบรารีบัสเหตุการณ์เช่นOttoสำหรับปัญหาประเภทนี้ เพียงสมัครส่วนประกอบที่ต้องการ (กิจกรรม):

protected void onResume() {
    super.onResume();
    bus.register(this);
}

จากนั้นระบุวิธีการโทรกลับ:

public void onTimeLeftEvent(TimeLeftEvent ev) {
    // process event..
}

จากนั้นเมื่อบริการของคุณดำเนินการคำสั่งเช่นนี้:

bus.post(new TimeLeftEvent(340));

POJO นั้นจะถูกส่งต่อไปยังกิจกรรมข้างต้นของคุณและส่วนประกอบการสมัครสมาชิกอื่น ๆ ทั้งหมด เรียบง่ายและสง่างาม


3

ฉันขอแนะนำให้ลองใช้รหัสต่อไปนี้:

    new Handler(Looper.getMainLooper()).post(() -> {

        //UI THREAD CODE HERE



    });

จำเป็นต้องมีคำอธิบายเพิ่มเติมเพื่อช่วยเหลือ OP
Moog

2

คุณสามารถรับค่าผ่านเครื่องรับสัญญาณออกอากาศ ...... ดังนี้ขั้นแรกให้สร้าง IntentFilter ของคุณเองเป็น

Intent intentFilter=new IntentFilter();
intentFilter.addAction("YOUR_INTENT_FILTER");

จากนั้นสร้างคลาสภายใน BroadcastReceiver เป็น

    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
    /** Receives the broadcast that has been fired */
    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent.getAction()=="YOUR_INTENT_FILTER"){
           //HERE YOU WILL GET VALUES FROM BROADCAST THROUGH INTENT EDIT YOUR TEXTVIEW///////////
           String receivedValue=intent.getStringExtra("KEY");
        }
    }
};

ตอนนี้ลงทะเบียนเครื่องรับสัญญาณออกอากาศของคุณใน onResume () เป็น

registerReceiver(broadcastReceiver, intentFilter);

และสุดท้ายยกเลิกการลงทะเบียน BroadcastReceiver ใน onDestroy () เป็น

unregisterReceiver(broadcastReceiver);

ตอนนี้ส่วนที่สำคัญที่สุด ... คุณต้องเริ่มการออกอากาศจากทุกที่ที่คุณต้องการส่งค่า ..... ดังนั้นทำตาม

Intent i=new Intent();
i.setAction("YOUR_INTENT_FILTER");
i.putExtra("KEY", "YOUR_VALUE");
sendBroadcast(i);

.... ไชโย :)


1

ในkotlinthats วิธีการที่คุณสามารถทำมันได้

สมมติว่าคุณต้องการแสดงข้อความ Toast จากบริการ

val handler = Handler(Looper.getMainLooper())
handler.post {
   Toast.makeText(context, "This is my message",Toast.LENGTH_LONG).show()
}

0

วิธีการแก้:

  1. สร้างHandlerด้วยLooperจาก Main Thread: requestHandler
  2. สร้างHandlerด้วยLooperจาก Main Thread: responseHandler และ override handleMessagemethod
  3. โพสต์งานRunnableตาม requestHandler
  4. ภายในRunnableงานโทร sendMessage ใน responseHandler
  5. sendMessageผลลัพธ์นี้เรียกใช้ handleMessage ใน responseHandler
  6. รับแอตทริบิวต์จากMessageและประมวลผลอัปเดต UI

โค้ดตัวอย่าง:

    /* Handler from UI Thread to send request */

    Handler requestHandler = new Handler(Looper.getMainLooper());

     /* Handler from UI Thread to process messages */

    final Handler responseHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {

            /* Processing handleMessage */

            Toast.makeText(MainActivity.this,
                    "Runnable completed with result:"+(String)msg.obj,
                    Toast.LENGTH_LONG)
                    .show();
        }
    };

    for ( int i=0; i<10; i++) {
        Runnable myRunnable = new Runnable() {
            @Override
            public void run() {
                try {
                   /* Send an Event to UI Thread through message. 
                      Add business logic and prepare message by 
                      replacing example code */

                    String text = "" + (++rId);
                    Message msg = new Message();

                    msg.obj = text.toString();
                    responseHandler.sendMessage(msg);
                    System.out.println(text.toString());

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