Android 기본 사항 : UI 스레드에서 코드 실행


450

UI 스레드에서 코드를 실행한다는 관점에서 다음과 같은 차이점이 있습니다.

MainActivity.this.runOnUiThread(new Runnable() {
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

또는

MainActivity.this.myView.post(new Runnable() {
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

private class BackgroundTask extends AsyncTask<String, Void, Bitmap> {
    protected void onPostExecute(Bitmap result) {
        Log.d("UI thread", "I am the UI thread");
    }
}

내 질문을 명확히하기 위해 : 서비스 코드, 일반적으로 리스너에서 해당 코드를 호출했다고 가정합니다. 또한 AsynkTask의 doInBackground () 함수 또는 처음 두 코드 조각 전에 호출 된 새 Task (...)에서 수행해야 할 많은 작업이 있다고 가정했습니다. 어쨌든 AsyncTask의 onPostExecute ()가 이벤트 큐의 끝에 놓입니다.
Luky

답변:


288

그것들이 모두 동일한 순 효과를 갖지만, 그것들 중 정확히 동일한 것은 없습니다.

제 1 및 제 2의 차이는가 될 일 경우이다 에서 코드를 실행시에 메인 프로그램 스레드, 첫 번째는 ( runOnUiThread())를 실행한다 Runnable바로. 두 번째 것 ( post()) Runnable은 이미 기본 애플리케이션 스레드에 있더라도 항상 이벤트 큐의 끝에 놓 입니다.

세 번째 인스턴스는 인스턴스를 만들고 실행한다고 가정하면 BackgroundTask스레드 풀에서 스레드를 가져 와서 기본 no-op을 실행 doInBackground()하기 전에 많은 양의 작업을 수행하기 전에 많은 시간을 낭비 합니다 post(). 이것은 세 가지 중 가장 효율적입니다. AsyncTask실제로 사용 하지 않고 백그라운드 스레드에서해야 할 작업이있는 경우 사용하십시오 onPostExecute().


27
또한주의 AsyncTask.execute()당신이로 배경 모든 작업을 이동하지 않는 한 단순히 배경 스레드에서 UI 스레드에서 코드를 실행하는 사용 사례에 대한 쓸모가이 옵션을 렌더링 어쨌든 UI 스레드에서 호출하도록 요구 doInBackground()하고 사용 AsyncTask제대로을.
kabuko

@ kabuko AsyncTaskUI 스레드에서 전화 하고 있는지 어떻게 확인할 수 있습니까?
Neil Galiaskarov

@NeilGaliaskarov 이것은 확실한 옵션처럼 보입니다 : stackoverflow.com/a/7897562/1839500
Dick Lucas

1
@NeilGaliaskarovboolean isUiThread = (Looper.getMainLooper().getThread() == Thread.currentThread());
금지

1
이상의 버전 이상에 대한 @NeilGaliaskarov 또는 M 사용과 동일Looper.getMainLooper().isCurrentThread
Balu Sangem에게

252

나는 HPP comment 에서 하나를 좋아하는데 , 매개 변수없이 어디서나 사용할 수 있습니다 :

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

2
이것이 얼마나 효율적입니까? 다른 옵션과 동일합니까?
EmmanuelMess

59

네 번째 방법은 Handler

new Handler().post(new Runnable() {
    @Override
    public void run() {
        // Code here will run in UI thread
    }
});

56
이 점에주의해야합니다. UI가 아닌 스레드에서 처리기를 만들면 UI가 아닌 스레드에 메시지를 게시하기 때문입니다. 기본적으로 핸들러는 작성된 스레드에 메시지를 게시합니다.
lujop

108
메인 UI 스레드 do에서 실행하려면 main을 정적 호출 new Handler(Looper.getMainLooper()).post(r)하는 바람직한 방법 Looper.getMainLooper()이지만 범위에 postOnUiThread()인스턴스가 있어야합니다 MainActivity.
HPP

1
@ HPP 나는이 방법을 몰랐다. 뷰가 없거나 활동이 없을 때 좋은 방법이 될 것이다. 잘 작동합니다! 정말 고마워요!
Sulfkain

@lujop AsyncTask의 onPreExecute 콜백 메소드와 동일합니다.
Sreekanth Karumanaghat

18

Pomber의 답변은 받아 들일 만하지 만 새로운 객체를 반복적으로 만드는 것에 열광하는 것은 아닙니다. 최상의 솔루션은 항상 메모리 호그를 완화하려는 솔루션입니다. 예, 자동 가비지 수집이 있지만 모바일 장치의 메모리 절약은 모범 사례의 범위에 속합니다. 아래 코드는 서비스에서 TextView를 업데이트합니다.

TextViewUpdater textViewUpdater = new TextViewUpdater();
Handler textViewUpdaterHandler = new Handler(Looper.getMainLooper());
private class TextViewUpdater implements Runnable{
    private String txt;
    @Override
    public void run() {
        searchResultTextView.setText(txt);
    }
    public void setText(String txt){
        this.txt = txt;
    }

}

다음과 같이 어디에서나 사용할 수 있습니다.

textViewUpdater.setText("Hello");
        textViewUpdaterHandler.post(textViewUpdater);

7
GC 및 객체 생성에 대해 +1 그러나 반드시 동의하지는 않습니다 The best solutions are always the ones that try to mitigate memory hog. 에 대한 다른 많은 기준 best이 있으며 약간의 냄새가납니다 premature optimization. 즉, 생성 된 객체 수가 문제 (응용 프로그램이 가비지를 생성하는 10,000 가지 다른 방법과 비교할 때)라고 충분히 알지 못한다면 best가장 간단한 것을 작성하는 것입니다 (가장 이해하기 쉽습니다) ) 코드를 작성하고 다른 작업으로 넘어갑니다.
ToolmakerSteve

2
BTW 는 일반적으로 post to main UI 스레드에 유용하기 때문에 or textViewUpdaterHandler와 같은 이름을 사용하는 것이 좋습니다 . TextViewUpdater 클래스에 전혀 연결되어 있지 않습니다. 코드의 나머지 부분에서 다른 곳으로 옮겨서 다른 곳에서 사용할 수 있음을 분명히합니다 ... 코드의 나머지 부분은 모호합니다. 하나의 객체를 동적으로 만들지 않기 때문에 단일 호출이 될 수있는 것을 깨뜨립니다. 임시로 사용하는 오래 지속되는 객체에 의존하는 두 단계 및 . 불필요하게 복잡하며 스레드로부터 안전하지 않습니다. 유지 관리가 쉽지 않습니다. uiHandlermainHandlersetTextpost
ToolmakerSteve

캐싱 및 가치가 너무 많은 상황 을 실제로 호출하는 경우 메소드 행 을 변경 하고 추가 하여 클래스를 개선 하면 호출자가 한 단계로 수행 할 수 있습니다 . 그런 다음 나중에 스레드로부터 안전해야하는 경우 메소드는 해당 명령문을 잠금 내부로 랩핑 할 수 있으며 호출자는 변경되지 않습니다. uiHandlertextViewUpdaterpublic void setText(String txt, Handler uiHandler)uiHandler.post(this);textViewUpdater.setText("Hello", uiHandler);uiHandler
ToolmakerSteve

Runnable을 한 번만 실행할 수 있다고 확신합니다. 어떤 아이디어를 절대적으로 깨뜨릴 수 있습니까?
Nativ

@Nativ Nope, Runnable은 여러 번 실행할 수 있습니다. 스레드는 할 수 없습니다.
Zbyszek

8

Android P부터 다음을 사용할 수 있습니다 getMainExecutor().

getMainExecutor().execute(new Runnable() {
  @Override public void run() {
    // Code will run on the main thread
  }
});

로부터 안드로이드 개발자 문서 :

이 문맥에 관련한 메인 thread로 큐에 넣어 진 태스크를 실행하는 Executor를 돌려줍니다. 응용 프로그램 구성 요소 (활동, 서비스 등)에 대한 호출을 디스패치하는 데 사용되는 스레드입니다.

로부터 CommonsBlog :

컨텍스트에서 getMainExecutor ()를 호출하여 기본 애플리케이션 스레드에서 작업을 실행할 실행자를 얻을 수 있습니다. Looper와 커스텀 Executor 구현을 사용하여 이것을 달성하는 다른 방법이 있지만, 이것은 더 간단합니다.


7

조각에 사용해야 할 경우 사용해야합니다

private Context context;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        this.context = context;
    }


    ((MainActivity)context).runOnUiThread(new Runnable() {
        public void run() {
            Log.d("UI thread", "I am the UI thread");
        }
    });

대신에

getActivity().runOnUiThread(new Runnable() {
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

호출기 조각과 같은 일부 상황에서는 널 포인터 예외가 있기 때문에


1

안녕 얘들 아,이 하나의 기본적인 질문은 내가 멀리 말해

핸들러 사용

new Handler().post(new Runnable() {
    @Override
    public void run() {
        // Code here will run in UI thread
    }
});

runOnUiThread를 통해 범용 핸들러를 사용하도록 선택한 이유가 있습니까?
ThePartyTurtle

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()UI 스레드에서 호출되지 않은 경우이를 제공합니다 .
Roc Boronat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.