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.