다른 스레드의 메인 스레드에서 코드 실행


326

안드로이드 서비스에서 백그라운드 작업을 수행하기위한 스레드를 만들었습니다.

스레드가 기본 스레드의 메시지 대기열에 특정 작업을 게시 해야하는 상황이 있습니다 (예 : a) Runnable.

Handler메인 스레드 를 가져 와서 다른 스레드에서 게시 Message/ 게시 하는 방법이 Runnable있습니까?

감사,


2
당신은 또한 사용자 정의 방송 수신기를 사용할 수 있습니다 .... 내 대답을 여기에 [내부 방송 수신기] [1] [1] : stackoverflow.com/a/22541324/1881527
Melbourne Lopes

많은 방법이 있습니다. David의 답변 및 dzeikei의 답변에 대한 답변 외에도 (3) 브로드 캐스트 수신기를 사용하거나 (4) 서비스를 시작하는 데 사용되는 Intent 이외의 핸들러를 전달한 다음 getIntent ( ) .getExtras ().
Ashok Bijoy Debnath

2
@ sazzad-hissain-khan, 왜 kotlin 태그를 사용하여 Java에서 주로 답변하는 2012 년의이 질문에 태그를 추가해야합니까?
Tenfour04

답변:


617

참고 :이 답변은 많은 주목을 받았으므로 업데이트해야합니다. 원래 답변이 게시 된 이후 @dzeikei의 의견은 원래 답변만큼이나 많은 주목을 받았습니다. 가능한 해결책은 다음과 같습니다.

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를 확장하고 handleMessage ()를 확장하면 주 스레드가 메시지를 처리하지 못하게 할 수 있습니다. 그것은 curosity에 대한 질문 일뿐입니다.
Ahmed

2
아니요. 서브 클래스를 Handler사용하거나 Handler.Callback인터페이스를 사용 handleMessage()하는 경우 핸들러를 사용하여 게시 된 메시지에 대해서만 메소드가 호출됩니다. 기본 스레드는 다른 핸들러를 사용하여 메시지를 게시 / 처리하므로 충돌이 없습니다.
David Wasser 2016 년

205
나는 당신이 사용한다면 상황이 필요 없을 것이라고 믿습니다Handler mainHandler = new Handler(Looper.getMainLooper());
dzeikei

6
사소한 점; 귀하의 코드는 현재 위치로 이동하지 않습니다 ... 그것은해야한다new Runnable(){@Override public void run() {....}};
리처드 설렘

1
@SagarDevanga 이것은 다른 질문을하기에 적합한 장소가 아닙니다. 관련이없는 답변에 대한 의견이 아닌 새로운 질문을 게시하십시오. 그런 식으로 더 좋고 빠른 응답을 얻을 수 있습니다.
David Wasser

138

아래의 주석가가 올바르게 지적했듯이 이것은 서비스의 일반적인 솔루션이 아니며 활동에서 시작된 스레드 (서비스는 그러한 스레드 일 수는 있지만 모든 스레드는 아닙니다)입니다. 서비스 활동 커뮤니케이션의 복잡한 주제에 대해서는 공식 문서의 전체 서비스 섹션을 읽으십시오-복잡하기 때문에 기본 사항을 이해하는 것이 좋습니다 : http://developer.android.com/guide/components/services.html # 알림

아래 방법은 가장 간단한 경우에 작동 할 수 있습니다.

올바르게 이해하면 응용 프로그램의 GUI 스레드에서 일부 코드를 실행해야합니다 ( "메인"스레드라는 다른 것을 생각할 수 없음). 이를 위해 다음과 같은 방법이 있습니다 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). 메인 루퍼에서 핸들러를 만듭니다.

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

2). Runnable 인터페이스를 구현하십시오.

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

삼). 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 Fair로 충분합니다. 왜 내가 지금 그 질문을했는지 (아직 1 년 전 게시 된) 기억이 나지 않습니다.
Greg Brown

24

압축 코드 블록은 다음과 같습니다.

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

여기에는 활동 참조 또는 응용 프로그램 참조를 전달하지 않아도됩니다.

코 틀린 동등 물 :

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

20

코 틀린 버전

당신이 활동에있는 다음 사용

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 안드로이드에서 일반 자바 스레드에 대한 더 나은 옵션입니다.

  1. HandlerThread를 작성 하고 시작하십시오.
  2. 크리에이트 핸들러루퍼 HandlerThread에서를 :requestHandler
  3. postRunnable작업에requestHandler

UI 스레드와의 통신 HandlerThread

  1. 기본 스레드에 대한 Handlerwith Looper를 작성하고 메소드를 responseHandler대체하십시오.handleMessage
  2. 내부 Runnable(다른 스레드의 작업 HandlerThread이 경우), 전화 sendMessageresponseHandler
  3. sendMessage결과 호출 handleMessage에서 responseHandler.
  4. 속성을 가져 Message와서 처리하고 UI를 업데이트하십시오.

: TextView웹 서비스에서받은 데이터로 업데이트 합니다. 웹 서비스는 UI가 아닌 스레드에서 호출되어야하므로 HandlerThread네트워크 작업을 위해 생성 됩니다. 웹 서비스에서 컨텐츠를 가져 오면 기본 스레드 (UI 스레드) 핸들러로 메시지를 보내면 메시지를 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);

유용한 기사 :

핸들러 스레드-왜 당신은 안드로이드 앱에서 그들을 사용해야합니까?

안드로이드 루퍼 처리기 처리기


2

나는 이것이 오래된 질문이라는 것을 알고 있지만 Kotlin과 Java 모두에서 사용하는 메인 스레드 원 라이너를 발견했습니다. 이것은 서비스에 가장 적합한 솔루션은 아니지만 프래그먼트 내부의 UI를 변경하는 무언가를 호출하는 경우 매우 간단하고 명백합니다.

자바 (8) :

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

코 틀린 :

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();

                   }
                });
            }
        });

서비스의 경우

oncreate에서 핸들러를 작성하십시오.

 handler = new Handler();

다음과 같이 사용하십시오

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

이 코드 스 니펫은 제한적이고 즉각적인 도움이 될 수 있습니다. 적절한 설명은 크게 장기 가치를 향상 할 보여줌으로써 이 문제에 대한 좋은 해결책이고, 다른 유사한 질문을 미래의 독자들에게 더 유용 할 것입니다. 제발 편집 당신이 만든 가정 등 일부 설명을 추가 할 답변을.
iBug

1

Kotlin의 경우 Anko corountines를 사용할 수 있습니다 .

최신 정보

doAsync {
   ...
}

더 이상 사용되지 않음

async(UI) {
    // Code run on UI thread
    // Use ref() instead of this@MyActivity
}

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.