การรันโค้ดในเธรดหลักจากเธรดอื่น


326

ในบริการ android ฉันได้สร้างเธรด (s) สำหรับการทำงานพื้นหลังบางอย่าง

Runnableฉันมีสถานการณ์ที่หัวข้อที่ต้องการที่จะโพสต์งานบางอย่างในคิวข้อความหัวข้อหลักของตัวอย่างที่

มีวิธีรับHandlerเธรดหลักและโพสต์Message/ Runnableไปยังเธรดอื่นหรือไม่

ขอบคุณ


2
นอกจากนี้คุณยังสามารถใช้เครื่องรับสัญญาณออกอากาศที่กำหนดเองได้ลองคำตอบของฉันที่นี่ [Inner Broadcast Receiver] [1] [1]: stackoverflow.com/a/22541324/1881527
Melbourne Lopes

มีหลายวิธี นอกเหนือจากคำตอบของเดวิด & ความคิดเห็นของ dzeikei ในคำตอบของเขา (3) คุณสามารถใช้ Broadcast Receiver หรือ (4) ส่ง handler ในส่วนเสริมของ Intent ที่ใช้ในการเริ่มบริการแล้วดึงตัวจัดการเธรดหลักภายในบริการโดยใช้ getIntent ( ) .getExtras ()
Ashok Bijoy Debnath

2
@ sazzad-hissain-khan, ทำไมติดแท็กคำถามนี้ตั้งแต่ปี 2012 ด้วยคำตอบส่วนใหญ่ใน Java ด้วยแท็ก kotlin?
Tenfour04

คำตอบ:


617

หมายเหตุ: คำตอบนี้ได้รับความสนใจอย่างมากซึ่งฉันจำเป็นต้องอัปเดต เนื่องจากคำตอบดั้งเดิมถูกโพสต์ความคิดเห็นจาก @dzeikei ได้รับความสนใจเกือบเท่ากับคำตอบดั้งเดิม ดังนั้นนี่คือคำตอบที่เป็นไปได้ 2 ข้อ:

1. ถ้าเธรดพื้นหลังของคุณมีการอ้างอิงถึงContextวัตถุ:

ตรวจสอบให้แน่ใจว่าเธรดผู้ทำงานเบื้องหลังของคุณมีการเข้าถึงวัตถุบริบท (อาจเป็นบริบทแอปพลิเคชันหรือบริบทบริการ) จากนั้นทำสิ่งนี้ในเธรดผู้ทำงานเบื้องหลัง:

// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(context.getMainLooper());

Runnable myRunnable = new Runnable() {
    @Override 
    public void run() {....} // This is your code
};
mainHandler.post(myRunnable);

2. ถ้าเธรดพื้นหลังของคุณไม่มีContextวัตถุ(หรือต้องการ)

(แนะนำโดย @dzeikei):

// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(Looper.getMainLooper());

Runnable myRunnable = new Runnable() {
    @Override 
    public void run() {....} // This is your code
};
mainHandler.post(myRunnable);

1
ขอบคุณ David มันได้ผลสำหรับฉันอีกอย่างหนึ่งถ้าคุณสามารถช่วยฉันได้ถ้าฉันขยาย Handler และ impl handleMessage () มันจะป้องกันไม่ให้เธรดหลักจัดการข้อความได้หรือไม่ นั่นเป็นเพียงคำถามที่ออกมาจากเงินยูโร ..
Ahmed

2
ไม่ถ้าคุณคลาสย่อยHandler(หรือใช้Handler.Callbackส่วนต่อประสาน) handleMessage()เมธอดของคุณจะถูกเรียกใช้เฉพาะสำหรับข้อความที่โพสต์โดยใช้ตัวจัดการของคุณ เธรดหลักใช้ตัวจัดการอื่นเพื่อโพสต์ / ประมวลผลข้อความดังนั้นจึงไม่มีข้อขัดแย้ง
David Wasser

205
ฉันเชื่อว่าคุณไม่จำเป็นต้องใช้บริบทหากคุณใช้Handler mainHandler = new Handler(Looper.getMainLooper());
dzeikei

6
จุดรอง; รหัสของคุณจะไม่ไปที่ ... ในขณะนี้ ควรเป็นnew Runnable(){@Override public void run() {....}};
Richard Tingle

1
@SagarDevanga นี่ไม่ใช่สถานที่ที่เหมาะสมที่จะถามคำถามอื่น โปรดโพสต์คำถามใหม่ไม่ใช่ความคิดเห็นในคำตอบที่ไม่เกี่ยวข้อง คุณจะได้รับการตอบสนองที่ดีขึ้นและเร็วขึ้น
David Wasser

138

ในฐานะที่เป็นผู้แสดงความคิดเห็นด้านล่างชี้ให้เห็นอย่างถูกต้องนี่ไม่ใช่โซลูชันทั่วไปสำหรับบริการเฉพาะสำหรับเธรดที่เปิดตัวจากกิจกรรมของคุณ (บริการอาจเป็นเธรด แต่ไม่ใช่ทั้งหมดที่มี) ในหัวข้อที่ซับซ้อนของการสื่อสารบริการกิจกรรมโปรดอ่านส่วนบริการทั้งหมดของเอกสารอย่างเป็นทางการ - มันซับซ้อนดังนั้นจึงต้องจ่ายเพื่อทำความเข้าใจพื้นฐาน: http://developer.android.com/guide/components/services.html #Notifications

วิธีการด้านล่างอาจใช้งานได้ในกรณีที่ง่ายที่สุด

ถ้าฉันเข้าใจคุณถูกต้องคุณต้องใช้รหัสบางอย่างในเธรด GUI ของแอปพลิเคชัน (ไม่สามารถคิดถึงสิ่งอื่นที่เรียกว่าเธรด "main") สำหรับสิ่งนี้มีวิธีการในActivity:

someActivity.runOnUiThread(new Runnable() {
        @Override
        public void run() {
           //Your code to run in GUI thread here
        }//public void run() {
});

หมอ: http://developer.android.com/reference/android/app/Activity.html#runOnUiThread%28java.lang.Runnable%29

หวังว่านี่คือสิ่งที่คุณกำลังมองหา


20
OP Serviceบอกว่าเขากำลังทำงานอยู่ในหัวข้อ คุณไม่สามารถใช้ในrunOnUiThread() Serviceคำตอบนี้ทำให้เข้าใจผิดและไม่ได้ตอบคำถามที่ถาม
David Wasser

31

มีอีกวิธีที่ง่ายถ้าคุณไม่มีสิทธิ์เข้าถึงบริบท

1) สร้างตัวจัดการจาก looper หลัก:

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

2) ใช้อินเทอร์เฟซ Runnable:

Runnable runnable = new Runnable() { // your code here }

3) โพสต์ Runnable ของคุณไปยัง uiHandler:

uiHandler.post(runnable);

นั่นคือทั้งหมด ;-) ขอให้สนุกกับเธรด แต่อย่าลืมซิงโครไนซ์


29

หากคุณเรียกใช้รหัสในเธรดเช่นทำการหน่วงเวลาการกระทำบางอย่างคุณจะต้องเรียกใช้runOnUiThreadจากบริบท ตัวอย่างเช่นหากรหัสของคุณอยู่ในMainActivityชั้นเรียนให้ใช้สิ่งนี้:

MainActivity.this.runOnUiThread(new Runnable() {
    @Override
    public void run() {
        myAction();
    }
});

หากวิธีการของคุณสามารถเรียกใช้จากหลัก (เธรด UI) หรือจากเธรดอื่นคุณต้องมีการตรวจสอบเช่น:

public void myMethod() {
   if( Looper.myLooper() == Looper.getMainLooper() ) {
       myAction();
   }
   else {

}

7
OP Serviceบอกว่าเขากำลังทำงานอยู่ในหัวข้อ คุณไม่สามารถใช้ในrunOnUiThread() Service
David Wasser

@DavidWasser มีการบันทึกไว้ที่ใดบ้างหรือไม่? เอกสารวิธีการไม่พูดถึงอะไรเกี่ยวกับมัน developer.android.com/reference/android/app/…
Greg Brown

1
@GregBrown ตามที่คุณระบุโดยการเชื่อมโยงของคุณไปยังActivityเอกสารเป็นวิธีการของrunOnUiThread() Activityมันไม่ใช่วิธีการServiceดังนั้นคุณไม่สามารถใช้งานได้ อะไรที่ชัดเจนกว่านั้น
David Wasser

@ DavidWasser ยุติธรรมเพียงพอ ฉันจำไม่ได้ว่าทำไมฉันถึงถามคำถามนั้นตอนนี้ (โพสต์เมื่อเกือบหนึ่งปีที่แล้ว)
Greg Brown

24

บล็อกรหัสย่อมีดังนี้:

   new Handler(Looper.getMainLooper()).post(new Runnable() {
       @Override
       public void run() {
           // things to do on the main thread
       }
   });

สิ่งนี้ไม่เกี่ยวข้องกับการอ้างอิงกิจกรรมหรือการอ้างอิงแอปพลิเคชัน

Kotlin เทียบเท่า:

    Handler(Looper.getMainLooper()).post(Runnable {
        // things to do on the main thread
    })

20

รุ่น Kotlin

เมื่อคุณอยู่ในกิจกรรมให้ใช้

runOnUiThread {
    //code that runs in main
}

เมื่อคุณมีบริบทกิจกรรม mContext แล้วใช้

mContext.runOnUiThread {
    //code that runs in main
}

เมื่อคุณอยู่ในที่ที่ไม่มีบริบทให้ใช้

Handler(Looper.getMainLooper()).post {  
    //code that runs in main
}

ไม่แน่ใจว่าสิ่งที่รุ่นของ Kotlin นี้ แต่ผมต้องเพิ่มวงเล็บ:runOnUiThread(){...}
WEX

5

วิธีที่ง่ายที่สุดโดยเฉพาะถ้าคุณไม่มีบริบทหากคุณใช้ RxAndroid คุณสามารถทำได้:

AndroidSchedulers.mainThread().scheduleDirect {
    runCodeHere()
}

5

รหัส Kotlin ที่แม่นยำยิ่งขึ้นโดยใช้ตัวจัดการ:

Handler(Looper.getMainLooper()).post {  
 // your codes here run on main Thread
 }

4

วิธีหนึ่งที่ฉันนึกได้คือ:

1) ให้ UI เชื่อมโยงกับบริการ
2) เปิดเผยวิธีการตามวิธีด้านล่างโดยการBinderลงทะเบียนของคุณHandler:

public void registerHandler(Handler handler) {
    mHandler = handler;
}

3) ในเธรด UI เรียกใช้วิธีการด้านบนหลังจากเชื่อมโยงกับบริการ:

mBinder.registerHandler(new Handler());

4) ใช้ตัวจัดการในเธรดของบริการเพื่อโพสต์งานของคุณ:

mHandler.post(runnable);

3

HandlerThread เป็นตัวเลือกที่ดีกว่าจาวา Threads ปกติใน Android

  1. สร้างHandlerThreadและเริ่มมัน
  2. สร้างตัวจัดการด้วยLooperจาก HandlerThread:requestHandler
  3. postRunnableงานในrequestHandler

การสื่อสารกับ UI Thread จาก HandlerThread

  1. สร้าง a Handlerด้วยLooperสำหรับเธรดหลัก: responseHandlerและhandleMessageวิธีการแทนที่
  2. ภายในRunnableภารกิจของเธรดอื่น ( HandlerThreadในกรณีนี้) ให้เรียกsendMessageใช้responseHandler
  3. นี้sendMessageผลอุทธรณ์ของในhandleMessageresponseHandler
  4. รับคุณลักษณะจากMessageและดำเนินการอัปเดต UI

ตัวอย่าง : อัปเดตTextViewด้วยข้อมูลที่ได้รับจากบริการเว็บ เนื่องจากบริการเว็บควรถูกเรียกใช้บนเธรดที่ไม่ใช่ UI ซึ่งสร้างขึ้นHandlerThreadสำหรับการทำงานของเครือข่าย เมื่อคุณได้รับเนื้อหาจากบริการบนเว็บส่งข้อความไปยังตัวจัดการเธรดหลัก (UI Thread) ของคุณและตัวจัดการนั้นHandlerจะจัดการข้อความและอัปเดต UI

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

HandlerThread handlerThread = new HandlerThread("NetworkOperation");
handlerThread.start();
Handler requestHandler = new Handler(handlerThread.getLooper());

final Handler responseHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        txtView.setText((String) msg.obj);
    }
};

Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        try {
            Log.d("Runnable", "Before IO call");
            URL page = new URL("http://www.your_web_site.com/fetchData.jsp");
            StringBuffer text = new StringBuffer();
            HttpURLConnection conn = (HttpURLConnection) page.openConnection();
            conn.connect();
            InputStreamReader in = new InputStreamReader((InputStream) conn.getContent());
            BufferedReader buff = new BufferedReader(in);
            String line;
            while ((line = buff.readLine()) != null) {
                text.append(line + "\n");
            }
            Log.d("Runnable", "After IO call:"+ text.toString());
            Message msg = new Message();
            msg.obj = text.toString();
            responseHandler.sendMessage(msg);


        } catch (Exception err) {
            err.printStackTrace();
        }
    }
};
requestHandler.post(myRunnable);

บทความที่มีประโยชน์:

handlerthreads และเหตุผลที่คุณควรจะกำลังจะใช้-พวกเขาในของคุณหุ่นยนต์ปพลิเคชัน

หุ่นยนต์ Looper จัดการ-handlerthread ฉัน


2

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

ชวา (8):

 getActivity().runOnUiThread(()->{
      //your main thread code
 });

Kotlin:

this.runOnUiThread {
     //your main thread code
}

1

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

AsyncTask.execute(new Runnable() {
            @Override
            public void run() {

            //code you want to run on the background
            someCode();

           //the code you want to run on main thread
 MainActivity.this.runOnUiThread(new Runnable() {

                    public void run() {

/*the code you want to run after the background operation otherwise they will executed earlier and give you an error*/
                        executeAfterOperation();

                   }
                });
            }
        });

ในกรณีของการบริการ

สร้างตัวจัดการในการสร้าง

 handler = new Handler();

จากนั้นใช้แบบนี้

 private void runOnUiThread(Runnable runnable) {
        handler.post(runnable);
    }

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


1

มีประโยชน์มากที่สุดคือการจัดเรียงของ:

import android.os.AsyncTask
import android.os.Handler
import android.os.Looper

object Dispatch {
    fun asyncOnBackground(call: ()->Unit) {
        AsyncTask.execute {
            call()
        }
    }

    fun asyncOnMain(call: ()->Unit) {
        Handler(Looper.getMainLooper()).post {
            call()
        }
    }
}

และหลังจากนั้น:

Dispatch.asyncOnBackground {
    val value = ...// super processing
    Dispatch.asyncOnMain { completion(value)}
}

0
public void mainWork() {
    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            //Add Your Code Here
        }
    });
}

สิ่งนี้สามารถทำงานในคลาสบริการโดยไม่มีปัญหา


ในขณะที่รหัสนี้อาจตอบคำถามคุณยังอาจพิจารณาเพิ่มประโยคอธิบายสองสามคำเนื่องจากนี่เป็นการเพิ่มมูลค่าของคำตอบของคุณสำหรับผู้ใช้รายอื่น!
MBT

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