source

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

nicesource 2023. 6. 8. 19:52
반응형

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 스레드에서:

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

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

당신은 그것을 노동자 스레드에서 부르고 있습니다.은 야합다니화에 .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();
}

기타 옵션:

사용할 수 있습니다. 만약 당신이 가지고 있다면 직설적으로.Activity:

@WorkerThread
void workerThread() {
    myActivity.runOnUiThread(() -> {
        // This is where your UI code goes.
    }
}

메인 루퍼에 글을 올릴 수도 있습니다.이것은 당신이 가진 것이 전부라면 잘 작동합니다.Context.

@WorkerThread
void workerThread() {
    ContextCompat.getMainExecutor(context).execute(()  -> {
        // This is where your UI code goes.
    }
}

사용되지 않음:

백그라운드에서 실행되는 대부분의 작업에 잘 작동하는 비동기 작업을 사용할 수 있습니다.이 도구에는 진행 상황과 완료 시기를 나타내는 후크가 있습니다.

편리하지만 올바르게 사용하지 않으면 컨텍스트가 유출될 수 있습니다.그것은 공식적으로 더 이상 사용되지 않으며, 더 이상 사용해서는 안 됩니다.

업데이트 - 2016

가장 좋은 대안은 다음을 사용하는 것입니다.RxAndroid에 대한 특정 :RxJava) 의PMVP데이터를 담당합니다.

반으로시작을 반환하는 것으로 합니다.Observable사용자의 기존 방법에서.

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

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

이 관찰 가능 항목을 다음과 같이 사용합니다.

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)

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

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

Ui 스레드에서 실행

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()를 호출한 다음 루프가 중지될 때까지 메시지를 처리하도록 루프()를 지정합니다.

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

비동기 작업

비동기 작업을 사용하면 사용자 인터페이스에서 비동기 작업을 수행할 수 있습니다.작업자 스레드에서 차단 작업을 수행한 다음 사용자가 직접 스레드 및/또는 처리기를 처리할 필요 없이 결과를 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
    }
}

핸들러

처리기를 사용하면 스레드의 메시지 큐와 관련된 메시지 및 실행 가능 개체를 보내고 처리할 수 있습니다.

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

Toast.makeText()기본/UI 스레드에서만 호출할 수 있습니다.Looper.getMainLooper()는 다음과 같은 이점을 제공합니다.

JAVA

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        // write your code here
    }
});

KOTLIN

Handler(Looper.getMainLooper()).post {
        // write your code here
}

이 방법의 장점은 활동이나 컨텍스트 없이 UI 코드를 실행할 수 있다는 것입니다.

런타임이 표시되면 시도해 보십시오.Looper로 인한 예외가 처리기 앞에 준비되지 않았습니다.

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

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

저도 같은 문제에 부딪혔는데, 이 문제를 해결한 방법은 다음과 같습니다.

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

UI 핸들러를 만들려면 다음을 수행해야 합니다.

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

이게 도움이 되길 바랍니다.

오류 이유:

worker 스레드는 백그라운드 작업을 수행하기 위한 것으로, runOnUiThread와 같은 메서드를 호출하지 않으면 worker 스레드 내의 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();    
   }
}); 

위의 코드를 사용하면 토스트 메시지가 다음과 같이 표시됩니다.UI thread당신이 그것을 속으로 부르고 있기 때문에.runOnUiThread방법.그래서 더 이상은java.lang.RuntimeException.

그게 제가 한 일입니다.

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

시각적 구성요소는 외부 스레드의 변경사항에 대해 "잠금"됩니다.따라서 토스트에는 메인 스레드에 의해 관리되는 메인 화면의 내용이 표시되므로 해당 스레드에서 이 코드를 실행해야 합니다.도움이 되길 바랍니다:)

다음을 수행하기 전까지 이 오류가 발생했습니다.

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

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

멋진 코틀린 솔루션:

runOnUiThread {
    // Add your ui thread code here
}

번째 전화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()

이는 작업자 스레드에서 Toast.makeText()가 호출되고 있기 때문입니다.메인 UI 스레드에서 이렇게 호출해야 합니다.

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

ChicoBird의 대답은 저에게 효과가 있었습니다.변경한 것은 UI 처리기를 만드는 것뿐입니다.

HandlerThread uiThread = new HandlerThread("UIHandler");

이클립스는 다른 어떤 것도 받아들이지 않았습니다.말이 되는군요.

한또.uiHandler분명하게 정의된 전역 클래스입니다.나는 여전히 안드로이드가 어떻게 이것을 하고 있고 무슨 일이 일어나고 있는지 이해할 수 없다고 주장하지만, 그것이 작동해서 기쁩니다.이제 저는 그것을 연구하고 안드로이드가 무엇을 하는지 이해할 수 있는지, 그리고 왜 사람들이 이 모든 어려움을 겪어야 하는지 알아보겠습니다.Chico Bird를 도와주셔서 감사합니다.

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

코루틴이 완벽하게 해낼 겁니다

CoroutineScope(Job() + Dispatchers.Main).launch {
                        Toast.makeText(context, "yourmessage",Toast.LENGTH_LONG).show()}

자바 8

new Handler(Looper.getMainLooper()).post(() -> {
    // Work in the UI thread

}); 

코틀린

Handler(Looper.getMainLooper()).post{
    // Work in the UI thread
}

GL

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

활동 - 활동 인스턴스 구성원 수준 - 을 사용하는 전용 방법으로 해결했습니다.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());

이제 핸들러 2는 메인 스레드와 다른 스레드를 사용하여 메시지를 처리합니다.

저도 같은 문제가 발생했고 이 코드는 현재 저에게 잘 작동하고 있습니다.
예를 들어 이것은 백그라운드 및 UI 스레드에서 작업을 수행하기 위한 내 코드입니다.
루퍼의 사용 방법을 관찰합니다.

new Thread(new Runnable() {
    @Override
    public void run() {
    Looper.prepare();
                                
    // your Background Task here

    runOnUiThread(new Runnable() {
        @Override
        public void run() {

        // update your UI here      
                                
        Looper.loop();
        }
    });
    }
}).start();

스레드에 대화상자 또는 토스터를 표시하려면 활동 개체를 사용하는 것이 가장 간단한 방법입니다.

예:

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

람다 사용:

activity.runOnUiThread(() -> Toast.makeText(activity, "Hello", Toast.LENGTH_SHORT).show());

토스트, AlertDialogs는 UI 스레드에서 실행되어야 하며, 비동기 작업을 사용하여 안드로이드 개발에서 올바르게 사용할 수 있습니다.그러나 시간 초과를 사용자 지정해야 하는 경우가 있으므로 스레드를 사용하지만 스레드에서는 비동기 작업에서 사용하는 것처럼 토스트, 알림 대화 상자를 사용할 수 없습니다.팝업을 위해 별도의 처리기가 필요합니다.

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

여기서 스위치 케이스를 사용했습니다. 같은 방식으로 다른 메시지를 표시해야 할 경우 핸들러 클래스 내에서 스위치 케이스를 사용할 수 있기 때문입니다.이것이 당신에게 도움이 되기를 바랍니다.

이것은 일반적으로 주 스레드의 어떤 것이 백그라운드 스레드에서 호출될 때 발생합니다.예를 들어, 예를 들어 보겠습니다.

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


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

위의 예에서는 작업자 스레드에서만 작동하는 doInBackground() 메서드의 메인 UI 스레드에 있는 텍스트 뷰에 텍스트를 설정하고 있습니다.

저도 같은 문제가 있었고 토스트를 포스트에 넣는 것만으로 간단히 해결했습니다.비동기 작업 <>의 실행() 재정의 기능이 작동했습니다.

당신은 UI 스레드에 토스트를 만들어야 합니다.아래 예제를 찾습니다.

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

토스트 메시지를 표시하려면 이 문서를 참조하십시오.

코루틴을 사용하는 코틀린을 위한 솔루션은 다음과 같습니다.

CoroutineScope by MainScope()로 클래스를 확장합니다.

class BootstrapActivity :  CoroutineScope by MainScope() {}

그런 다음 간단하게 다음을 수행합니다.

launch {
        // whatever you want to do in the main thread
    }

코루틴에 대한 종속성을 추가하는 것을 잊지 마십시오.

org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.kotlinCoroutines}
org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.kotlinCoroutines}

스레드 외부에 처리기 만들기

final Handler handler = new Handler();

        new Thread(new Runnable() {
            @Override
            public void run() {
            try{
                 handler.post(new Runnable() {
                        @Override
                        public void run() {
                            showAlertDialog(p.getProviderName(), Token, p.getProviderId(), Amount);
                        }
                    });

                }
            }
            catch (Exception e){
                Log.d("ProvidersNullExp", e.getMessage());
            }
        }
    }).start();

최근에 이런 문제가 발생했습니다. 생성자의 UI 작업을 수행하는 함수를 호출하려고 했기 때문입니다.생성자에서 초기화를 제거하여 문제를 해결했습니다.

다음 코드를 사용하여 비 메인 스레드 "session"의 메시지를 표시합니다.

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

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

반응형