Looper.prepare ()를 호출하지 않은 스레드 내에서 핸들러를 작성할 수 없습니다.


981

다음 예외는 무엇을 의미합니까? 어떻게 고칠 수 있습니까?

이것은 코드입니다.

Toast toast = Toast.makeText(mContext, "Something", Toast.LENGTH_SHORT);

이것은 예외입니다 :

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
     at android.os.Handler.<init>(Handler.java:121)
     at android.widget.Toast.<init>(Toast.java:68)
     at android.widget.Toast.makeText(Toast.java:231)

8
이 라이브러리를 확인하십시오. compile 'com.shamanland:xdroid-toaster:0.0.5'필수 runOnUiThread()또는 Context변수 가 아니며 모든 루틴이 사라졌습니다! 단지 호출 Toaster.toast(R.string.my_msg);여기에 예입니다 github.com/shamanland/xdroid-toaster-example
— Oleksii K.

120
어리석은 오류 메시지! UI가 아닌 스레드에서 뷰를 만질 때처럼 UI가 아닌 스레드에서 이것을 호출 할 수없는 것처럼 간단 할 수 있습니다.
— Dheeraj Bhaskar

11
다른 코드에서 동일한 예외 메시지를받는 사람 : 예외 메시지의 의미는 Looper를 준비하지 않은 스레드를 통해 코드를 호출한다는 것입니다. 일반적으로 UI 스레드에서 호출하는 것이 아니라 호출해야 함을 의미하지만 (OP의 경우) 정상 스레드는 루퍼를 준비하지 않지만 UI 스레드는 항상 수행합니다.
— Helin Wang

@OleksiiKropachov 언급 한 라이브러리의 구현은 runOnUiThread ()를 수행하는 것과 매우 유사합니다.
— Helin Wang

예, 그러나 매우 유용한 래퍼입니다.
— Oleksii K.

답변:


696

작업자 스레드에서 호출합니다. Toast.makeText()메인 스레드 내에서 (그리고 UI를 다루는 대부분의 다른 함수들) 을 호출해야합니다 . 예를 들어 핸들러를 사용할 수 있습니다.

문서에서 UI 스레드와의 통신을 찾으십시오 . 간단히 말해서 :

// Set this up in the UI thread.

mHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message message) {
        // This is where you do your work in the UI thread.
        // Your worker tells you in the message what to do.
    }
};

void workerThread() {
    // And this is how you call it from the worker thread:
    Message message = mHandler.obtainMessage(command, parameter);
    message.sendToTarget();
}

다른 옵션:

AsyncTask를 사용할 수 있습니다백그라운드에서 실행되는 대부분의 작업에 잘 작동 . 진행 상황과 완료 시점을 표시하기 위해 호출 할 수있는 후크가 있습니다.

당신은 또한 사용할 수 있습니다 Activity.runOnUiThread () .


원래 문제는 어떻습니까 (AlertDialog가 아님)?
— Ivan G.

5
Cleggy가 말한 것에 2 센트 만 추가하면됩니다. 코드화 된 예제가 종종 그 자체로 볼륨을 말할 수 있으므로 의미하는 바에 대해 간단히 설명하는 것이 좋습니다.
— cdata


3
GUI를 지원하는 거의 모든 프로그래밍 언어 AFAIK에서 GUI와 직접 업데이트 / 변경 / 표시 / 상호 작용하는 경우 프로그램의 기본 스레드에서 수행해야합니다.
— Ahmed

(and most other functions dealing with the UI)백그라운드에서 사용할 수있는 UI 함수의 예 android.support.design.widget.Snackbar는 UI 스레드에서 호출하지 않을 때 기능이 저하되지 않는 것입니다.
— Scruuffy

852

Toast.makeText(...)UI 스레드에서 호출해야합니다 .

activity.runOnUiThread(new Runnable() {
  public void run() {
    Toast.makeText(activity, "Hello", Toast.LENGTH_SHORT).show();
  }
});

이것은 다른 (중복) SO 답변 에서 복사하여 붙여 넣습니다 .


13
좋은 대답입니다. 이것은 잠시 동안 혼란 스러웠습니다. 참고로, 나는 활동이 필요하지 않았습니다. runOnUiThread 전에.
— Cen92

448

업데이트-2016

가장 좋은 대안은 데이터를 처리하기 위해 RxAndroid(에 대한 특정 바인딩 RxJava)을 사용하는 것 P입니다 MVP.

Observable기존 방법에서 복귀 하여 시작하십시오 .

private Observable<PojoObject> getObservableItems() {
    return Observable.create(subscriber -> {

        for (PojoObject pojoObject: pojoObjects) {
            subscriber.onNext(pojoObject);
        }
        subscriber.onCompleted();
    });
}

이 Observable을 다음과 같이 사용하십시오-

getObservableItems().
subscribeOn(Schedulers.io()).
observeOn(AndroidSchedulers.mainThread()).
subscribe(new Observer<PojoObject> () {
    @Override
    public void onCompleted() {
        // Print Toast on completion
    }

    @Override
    public void onError(Throwable e) {}

    @Override
    public void onNext(PojoObject pojoObject) {
        // Show Progress
    }
});
}

-------------------------------------------------- -------------------------------------------------- ------------------------------

나는 내가 조금 늦었다는 것을 알고 있지만 여기에 간다. 안드로이드는 기본적으로 UI 스레드 와 백그라운드 스레드의 두 가지 스레드 유형에서 작동 합니다 . 안드로이드 문서에 따르면-

이 문제를 해결하기 위해 UI 스레드 외부에서 Android UI 툴킷에 액세스하지 마십시오. Android는 다른 스레드에서 UI 스레드에 액세스하는 여러 가지 방법을 제공합니다. 다음은 도움이 될 수있는 방법 목록입니다.

Activity.runOnUiThread(Runnable)  
View.post(Runnable)  
View.postDelayed(Runnable, long)

이제이 문제를 해결하는 다양한 방법이 있습니다.

코드 샘플로 설명하겠습니다.

runOnUiThread

new Thread()
{
    public void run()
    {
        myactivity.this.runOnUiThread(new Runnable()
        {
            public void run()
            {
                //Do your UI operations like dialog opening or Toast here
            }
        });
    }
}.start();

자벌레

스레드에 대한 메시지 루프를 실행하는 데 사용되는 클래스입니다. 스레드에는 기본적으로 메시지 루프가 없습니다. 하나를 만들려면 루프를 실행할 스레드에서 Preparing ()를 호출 한 다음 loop ()를 호출하여 루프가 중지 될 때까지 메시지를 처리하도록합니다.

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
    }
}

비동기 작업

AsyncTask를 사용하면 사용자 인터페이스에서 비동기 작업을 수행 할 수 있습니다. 작업자 스레드에서 차단 작업을 수행 한 다음 사용자가 스레드 및 / 또는 처리기를 직접 처리하지 않고도 UI 스레드에 결과를 게시합니다.

public void onClick(View v) {
    new CustomTask().execute((Void[])null);
}


private class CustomTask extends AsyncTask<Void, Void, Void> {

    protected Void doInBackground(Void... param) {
        //Do some work
        return null;
    }

    protected void onPostExecute(Void param) {
        //Print Toast or open dialog
    }
}

매니저

핸들러를 사용하면 스레드의 MessageQueue와 연관된 Message 및 Runnable 객체를 보내고 처리 할 수 ​​있습니다.

Message msg = new Message();


new Thread()
{
    public void run()
    {
        msg.arg1=1;
        handler.sendMessage(msg);
    }
}.start();



Handler handler = new Handler(new Handler.Callback() {

    @Override
    public boolean handleMessage(Message msg) {
        if(msg.arg1==1)
        {
            //Print Toast or open dialog        
        }
        return false;
    }
});

7
이것이 바로 내가 찾던 것입니다. 특히, 제 1 예runOnUiThread
— 나빈

5
고마워, 5 년의 안드로이드 프로그래밍과 나는 View또한 방법 post(Runnable)과 알고 있다 postDelayed(Runnable, long)! 너무 많은 처리기가 헛된 것입니다. :)
— Fenix ​​Voltres

Handler 예제로 혼란스러워하는 사람들을 위해 : "new Handler (callback)"가 바인딩되는 스레드는 무엇입니까? 핸들러를 만든 스레드에 바인딩됩니다.
— Helin Wang

1
이것이 왜 가장 좋은 대안 입니까?
— IgorGanapolsky

doInBackground를 usw하고 ArrayList를 다시 가져 오려고하지만 항상 오류가 발생합니다. Looper.prepare ()를 호출하지 않은 스레드 내에서 핸들러를 만들 수 없습니다. 이것이 내 질문입니다 stackoverflow.com/questions/45562615/… 그러나이 답변에서 해결책을 얻을 수 없습니다
— WeSt

120

Toast.makeText()Main / UI 스레드에서만 호출해야합니다. Looper.getMainLooper ()는 이를 달성하는 데 도움이됩니다.

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        Toast toast = Toast.makeText(mContext, "Something", Toast.LENGTH_SHORT);
    }
});

이 방법의 장점은 Activity 또는 Context없이 사용할 수 있다는 것입니다.


2
다른 답변이 저에게 효과가 없었습니다. 지속성을 관리하기 위해 라이브러리 설탕 레코드를 사용하고 있습니다. 그리고 그 안에 나는 활동이 없습니다. 그러나 이것은 훌륭하게 작동합니다
— cabaji99

91

처리기 전에 준비되지 않은 Looper로 인해 runtimeException이 표시되면이를 시도하십시오.

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

handler.postDelayed(new Runnable() {
  @Override
  public void run() {
  // Run your task here
  }
}, 1000 );

핸들러는 추상 클래스입니다. 이 컴파일되지 않습니다
— 스텔스 랍비에게

2
@StealthRabbi import Handler 올바른 네임 스페이스에서 가져 오기android.os.Handler
— NightFury

이것은 문제가되지 않을 수 있습니다. 호출 클래스, 기간에서 루 퍼가 존재하지 않을 수 있습니다.
— IgorGanapolsky

42

나는 같은 문제에 부딪쳤다.

private final class UIHandler extends Handler
{
    public static final int DISPLAY_UI_TOAST = 0;
    public static final int DISPLAY_UI_DIALOG = 1;

    public UIHandler(Looper looper)
    {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg)
    {
        switch(msg.what)
        {
        case UIHandler.DISPLAY_UI_TOAST:
        {
            Context context = getApplicationContext();
            Toast t = Toast.makeText(context, (String)msg.obj, Toast.LENGTH_LONG);
            t.show();
        }
        case UIHandler.DISPLAY_UI_DIALOG:
            //TBD
        default:
            break;
        }
    }
}

protected void handleUIRequest(String message)
{
    Message msg = uiHandler.obtainMessage(UIHandler.DISPLAY_UI_TOAST);
    msg.obj = message;
    uiHandler.sendMessage(msg);
}

UIHandler를 작성하려면 다음을 수행해야합니다.

    HandlerThread uiThread = new HandlerThread("UIHandler");
    uiThread.start();
    uiHandler = new UIHandler((HandlerThread) uiThread.getLooper());

도움이 되었기를 바랍니다.


코드를 사용하려고했지만 잃어 버렸고 onCreate method상황에 따라 AsyncTask에서 전화를 걸 거나받는 방법을 잘 모르겠습니다. 일이 어떻게 작동하는지 배우기 위해 전체 코드를 게시 하시겠습니까?
— Nick Kahn

2
마지막 줄을 읽지 않아야 uiHandler = new UIHandler(uiThread.getLooper()); 합니까?
— Beer Me

36

오류 이유 :

워커 스레드는 백그라운드 작업을 수행하기위한 것이며 runOnUiThread 와 같은 메서드를 호출하지 않으면 워커 스레드 내의 UI에 아무것도 표시 할 수 없습니다 . runOnUiThread를 호출하지 않고 UI 스레드에 아무 것도 표시하려고하면java.lang.RuntimeException .

따라서 작업자 스레드에서 activity전화 Toast.makeText()를 거는 경우 다음을 수행하십시오.

runOnUiThread(new Runnable() 
{
   public void run() 
   {
      Toast toast = Toast.makeText(getApplicationContext(), "Something", Toast.LENGTH_SHORT).show();    
   }
}); 

위의 코드는 Toast 메시지를 메소드 UI thread내부에서 호출하기 때문에 표시합니다 runOnUiThread. 더 이상은 없습니다 java.lang.RuntimeException.


23

그게 내가 한 일입니다.

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        Toast(...);
    }
});

시각적 구성 요소는 외부 스레드의 변경에 "고정"됩니다. 따라서 토스트는 메인 스레드에서 관리하는 메인 화면에 내용을 표시하므로 해당 스레드에서이 코드를 실행해야합니다. 희망이 있습니다 :)


나는이 같은 방법을 사용했다. 그러나 Runnable의 익명 내부 클래스가 활동에 대한 암시 적 참조를 보유하기 때문에 이것이 누출 가능성을 열어 둡니다.
— 피터 G. 윌리엄스

1
좋은 지적입니다 :) 그냥 getApplicationContext () 또는 이와 비슷한 것을 사용하여 안전한면에 두십시오. 내가 아는 코드에는 아무런 문제가
— 없었지만

22

다음을 수행 할 때 까지이 오류가 발생했습니다.

public void somethingHappened(final Context context)
{
    Handler handler = new Handler(Looper.getMainLooper());
    handler.post(
        new Runnable()
        {
            @Override
            public void run()
            {
                Toast.makeText(context, "Something happened.", Toast.LENGTH_SHORT).show();
            }
        }
    );
}

그리고 이것을 싱글 톤 클래스로 만들었습니다.

public enum Toaster {
    INSTANCE;

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

    public void postMessage(final String message) {
        handler.post(
            new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(ApplicationHolder.INSTANCE.getCustomApplication(), message, Toast.LENGTH_SHORT)
                        .show();
                }
            }
        );
    }

}

Toaster를 어디에서 사용 하십니까? 첫 번째 스 니펫에서는 사용되지 않습니다 ...
— IgorGanapolsky

1
Toaster.INSTANCE.postMessage(ResourceUtils.getString(R.string.blah));한동안 토스트를 사용하지 않았지만, 내가 사용했던 편의 클래스 (길이를 알고있다가 나중에 줄였습니다)
— EpicPandaForce

그래서 무엇을 ApplicationHolder.INSTANCE평가합니까?
— IgorGanapolsky

프로세스가 존재하는 동안 응용 프로그램이 항상 존재한다는 것을 고려 CustomApplication하여 의에 설정된 정적 변수는 CustomApplication.onCreate()이 컨텍스트를 전 세계적으로 사용할 수 있습니다.
— EpicPandaForce

11
 runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(mContext, "Message", Toast.LENGTH_SHORT).show();
            }
        });

2
이것은 나를 위해 일했고 나는 람다를 사용합니다runOnUiThread(() -> { Toast toast = Toast.makeText(getApplicationContext(), "Message", Toast.LENGTH_SHORT); toast.show(); });
— Black_Zerg


9

Toast.makeText ()가 작업자 스레드에서 호출하기 때문입니다. 이 같은 메인 UI 스레드에서 호출해야합니다

runOnUiThread(new Runnable() {
      public void run() {
        Toast toast = Toast.makeText(mContext, "Something", Toast.LENGTH_SHORT);
      }
 });

8

ChicoBird의 답변이 저에게 효과적이었습니다. 내가 한 유일한 변경 사항은 내가해야 할 UIHandler를 만드는 것입니다.

HandlerThread uiThread = new HandlerThread("UIHandler");

이클립스는 다른 어떤 것도 받아들이지 않았다. 내가 생각하는 것은 말이된다.

또한 uiHandler어딘가에 정의 된 클래스 글로벌입니다. 나는 아직도 안드로이드가 어떻게하고 있는지 이해하고 있다고 주장하지는 않지만 어떻게 작동하는지 기쁘게 생각합니다. 이제 나는 그것을 공부하고 안드로이드가 무엇을하고 있는지, 왜이 모든 고리와 고리를 거쳐야하는지 이해할 수 있는지 알아볼 것입니다. ChicoBird의 도움에 감사드립니다.


6

첫 번째 전화 Looper.prepare()를 한 다음 Toast.makeText().show()마지막 전화를 다음과 같이 호출 하십시오 Looper.loop().

Looper.prepare() // to be able to make toast
Toast.makeText(context, "not connected", Toast.LENGTH_LONG).show()
Looper.loop()

이 답변이 과소 평가되는 이유는 무엇입니까?
— DkPathak

5

Rxjava 및 RxAndroid 사용자의 경우 :

public static void shortToast(String msg) {
    Observable.just(msg)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(message -> {
                Toast.makeText(App.getInstance(), message, Toast.LENGTH_SHORT).show();
            });
}

괄호는 불필요합니다.
— Borja

4

콜백에서 대화 상자를 표시하려고 할 때 동일한 문제가 발생했습니다.

활동에 - 나는 활동에 헌신적 인 방법으로 해결 인스턴스 멤버 레벨 - 그 사용을runOnUiThread(..)

public void showAuthProgressDialog() {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mAuthProgressDialog = DialogUtil.getVisibleProgressDialog(SignInActivity.this, "Loading ...");
        }
    });
}

public void dismissAuthProgressDialog() {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            if (mAuthProgressDialog == null || ! mAuthProgressDialog.isShowing()) {
                return;
            }
            mAuthProgressDialog.dismiss();
        }
    });
}

2
Handler handler2;  
HandlerThread handlerThread=new HandlerThread("second_thread");
handlerThread.start();
handler2=new Handler(handlerThread.getLooper());

이제 handler2는 주 스레드와 다른 스레드를 사용하여 메시지를 처리합니다.


1

대화 상자 또는 토스터를 스레드에 표시하려면 가장 간결한 방법은 Activity 객체를 사용하는 것입니다.

예를 들면 다음과 같습니다.

new Thread(new Runnable() {
    @Override
    public void run() {
        myActivity.runOnUiThread(new Runnable() {
            public void run() {
                myActivity.this.processingWaitDialog = new ProgressDialog(myActivity.this.getContext());
                myActivity.this.processingWaitDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
                myActivity.this.processingWaitDialog.setMessage("abc");
                myActivity.this.processingWaitDialog.setIndeterminate(true);
                myActivity.this.processingWaitDialog.show();
            }
        });
        expenseClassify.serverPost(
                new AsyncOperationCallback() {
                    public void operationCompleted(Object sender) {
                        myActivity.runOnUiThread(new Runnable() {
                            public void run() {
                                if (myActivity.this.processingWaitDialog != null 
                                        && myActivity.this.processingWaitDialog.isShowing()) {
                                    myActivity.this.processingWaitDialog.dismiss();
                                    myActivity.this.processingWaitDialog = null;
                                }
                            }
                        }); // .runOnUiThread(new Runnable()
...

0

토스트, AlertDialogs UI 스레드에서 실행해야합니다, 당신은 사용할 수 있습니다 AsyncTask를을 우리가 사용하는, 그래서 우리는 시간 초과를 사용자 정의해야 할 경우 development.but 안드로이드에 적절하게 사용하는 스레드를 ,하지만 스레드에서 우리는 우리가 사용하는 것과 토스트, Alertdialogs을 사용할 수 없습니다 팝업을 위해 별도의 핸들러 가 필요합니다 .

public void onSigned() {
    Thread thread = new Thread(){
        @Override
        public void run() {
            try{
                sleep(3000);
                Message message = new Message();
                message.what = 2;
                handler.sendMessage(message);
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    };
    thread.start();
}

위의 예제에서 3 초 안에 스레드를 잠자기하고 메인 스레드 구현 핸들러 에서 토스트 메시지를 표시하려고 합니다.

handler = new Handler() {
       public void handleMessage(Message msg) {
           switch(msg.what){
              case 1:
              Toast.makeText(getActivity(),"cool",Toast.LENGTH_SHORT).show();
              break;
           }
           super.handleMessage(msg);
       }
};

여기에 스위치 케이스를 사용했습니다. 같은 방식으로 다른 메시지를 표시 해야하는 경우 핸들러 클래스 내에서 스위치 케이스를 사용할 수 있습니다.


0

이것은 주로 메인 스레드의 어떤 것이 백그라운드 스레드에서 호출 될 때 발생합니다. 예를 들어 보자.

private class MyTask extends AsyncTask<Void, Void, Void> {


@Override
protected Void doInBackground(Void... voids) {
        textView.setText("Any Text");
        return null;
    }
}

위의 예제에서는 작업자 스레드에서만 작동하는 doInBackground () 메서드의 기본 UI 스레드에있는 textview에 텍스트를 설정합니다.


0

나는 같은 문제가 있었고 Asynctask <>의 onPostExecute () 재정의 함수에 Toast를 넣어 간단하게 문제를 해결했습니다.


-2

다음 코드를 사용하여 메인 스레드가 아닌 "컨텍스트"의 메시지를 표시합니다

@FunctionalInterface
public interface IShowMessage {
    Context getContext();

    default void showMessage(String message) {
        final Thread mThread = new Thread() {
            @Override
            public void run() {
                try {
                    Looper.prepare();
                    Toast.makeText(getContext(), message, Toast.LENGTH_LONG).show();
                    Looper.loop();
                } catch (Exception error) {
                    error.printStackTrace();
                    Log.e("IShowMessage", error.getMessage());
                }
            }
        };
        mThread.start();
    }
}

다음과 같이 사용하십시오.

class myClass implements IShowMessage{

  showMessage("your message!");
 @Override
    public Context getContext() {
        return getApplicationContext();
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.