programing

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

nasanasas 2020. 9. 28. 09:16
반응형

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


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

다음은 코드입니다.

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)

작업자 스레드에서 호출하고 있습니다. 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 () 사용할 수도 있습니다 .


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

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

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


업데이트-2016

가장 좋은 대안은 사용하는 것입니다 RxAndroid(특정 바인딩을 RxJava용) PMVP데이터 FO 취할 충전.

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

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

나는 내가 조금 늦었다는 것을 알고 있지만 여기에 간다. Android는 기본적으로 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();

자벌레

스레드에 대한 메시지 루프를 실행하는 데 사용되는 클래스입니다. 기본적으로 스레드에는 연관된 메시지 루프가 없습니다. 하나를 만들려면 루프를 실행할 스레드에서 prepare ()를 호출 한 다음 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

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

핸들러 전에 준비되지 않은 Looper로 인해 runtimeException이 발생하면 이것을 시도하십시오.

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

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

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없이) 클래스에서 사용할 수 있다는 것입니다.


나는 같은 문제에 부딪 혔고 여기에 내가 그것을 고친 방법이 있습니다.

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

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


오류 이유 :

작업자 스레드는 백그라운드 작업을 수행하기위한 것이며 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내에서 호출하기 때문에 a에 표시되도록합니다 runOnUiThread. 그래서 더 이상 java.lang.RuntimeException.


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

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

}

그게 내가 한 일입니다.

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

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


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

이는 Toast.makeText ()가 작업자 스레드에서 호출되기 때문입니다. 다음과 같은 기본 UI 스레드에서 호출해야합니다.

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

ChicoBird의 답변이 저에게 효과적이었습니다. 내가 만든 유일한 변경 사항은 UIHandler를 만들 때였습니다.

HandlerThread uiThread = new HandlerThread("UIHandler");

Eclipse는 다른 것을 받아들이기를 거부했습니다. 말이 돼요.

또한 uiHandler어딘가에 정의 된 전역 클래스입니다. 나는 아직도 안드로이드가 어떻게이 일을하고 있고 무슨 일이 일어나고 있는지 이해한다고 주장하지 않지만 그것이 작동한다는 것은 기쁘다. 이제 나는 그것을 연구하고 안드로이드가 무엇을하고 있는지 그리고 왜 이러한 모든 고리와 반복을 거쳐야 하는지를 이해할 수 있는지 살펴볼 것입니다. ChicoBird를 도와 주셔서 감사합니다.


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

멋진 Kotlin 솔루션 :

runOnUiThread {
    // Add your ui thread code here
}

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

나는 Activity의 전용 메서드를 사용하여 해결했습니다.-Activity 인스턴스 멤버 수준에서 -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();
        }
    });
}

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

이제 handler2는 메인 스레드가 아닌 다른 스레드를 사용하여 메시지를 처리합니다.


첫 번째 호출 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()

To display a dialog or a toaster in a thread, the most concise way is to use the Activity object.

For example:

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

Toast, AlertDialogs needs to run on UI thread, you can use Asynctask to use them properly in android development.but some cases we need to customize the time outs, so we use Threads, but in threads we cannot use Toast,Alertdialogs like we using in AsyncTask.So we need separate Handler for popup those.

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

in Above example i want to sleep my thread in 3sec and after i want to show a Toast message,for that in your mainthread implement handler.

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

I used switch case here, because if you need to show different message in same way, you can use switch case within Handler class...hope this will help you


This usually happens when something on the main thread is called from any background thread. Lets look at an example , for instance.

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


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

In the above example , we are setting text on the textview which is in the main UI thread from doInBackground() method , which operates only on a worker thread.


I had the same problem and I fixed it simply by putting the Toast in onPostExecute() override function of the Asynctask<> and it worked.


i use the following code to show message from non main thread "context",

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

then use as the following:

class myClass implements IShowMessage{

  showMessage("your message!");
 @Override
    public Context getContext() {
        return getApplicationContext();
    }
}

참고URL : https://stackoverflow.com/questions/3875184/cant-create-handler-inside-thread-that-has-not-called-looper-prepare

반응형