Was bedeutet die folgende Ausnahme? wie kann ich es reparieren?
Dies ist der Code:
Toast toast = Toast.makeText(mContext, "Something", Toast.LENGTH_SHORT);
Dies ist die Ausnahme:
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)
Sie nennen es von einem Arbeitsthread aus. Sie müssen Toast.makeText()
(und die meisten anderen Funktionen, die sich auf die Benutzeroberfläche beziehen) vom Haupt-Thread aus aufrufen. Sie könnten beispielsweise einen Handler verwenden.
Nachschlagen Kommunizieren mit dem UI-Thread in der Dokumentation. In einer Nussschale:
// 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();
}
Andere Optionen:
Sie könnten eine AsyncTask verwenden, die für die meisten Dinge im Hintergrund gut funktioniert. Es gibt Haken, die Sie anrufen können, um den Fortschritt anzuzeigen und wann es fertig ist.
Sie können auch Activity.runOnUiThread () verwenden.
Sie müssen Toast.makeText(...)
vom UI-Thread aus aufrufen:
activity.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(activity, "Hello", Toast.LENGTH_SHORT).show();
}
});
Dies wird kopiert von einer anderen (Duplikat) SO - Antwort .
Die beste Alternative ist, RxAndroid
(spezifische Bindungen für RxJava
) für P
in MVP
zu verwenden, um Daten zu übernehmen.
Beginnen Sie mit der Rückgabe von Observable
aus Ihrer vorhandenen Methode.
private Observable<PojoObject> getObservableItems() {
return Observable.create(subscriber -> {
for (PojoObject pojoObject: pojoObjects) {
subscriber.onNext(pojoObject);
}
subscriber.onCompleted();
});
}
Verwenden Sie dieses Observable so -
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
}
});
}
------------------------------------------------ -------------------------------------------------- --------------------------------
Ich weiß, ich bin etwas spät dran, aber hier geht es weiter ... __ Android arbeitet grundsätzlich an zwei Thread-Typen, nämlich UI-Threadund Hintergrund-Thread. Laut Android-Dokumentation -
Greifen Sie nicht außerhalb des UI-Threads auf das Android UI-Toolkit zu, um dieses Problem zu beheben. Android bietet mehrere Möglichkeiten, auf den UI-Thread von anderen Threads zuzugreifen. Hier ist eine Liste von Methoden, die helfen können:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
Nun gibt es verschiedene Methoden, um dieses Problem zu lösen.
Ich werde es anhand eines Codebeispiels erklären:
new Thread()
{
public void run()
{
myactivity.this.runOnUiThread(new Runnable()
{
public void run()
{
//Do your UI operations like dialog opening or Toast here
}
});
}
}.start();
Klasse, die zum Ausführen einer Nachrichtenschleife für einen Thread verwendet wird. Standardmäßig tun Threads keine Nachrichtenschleife zugeordnet ist; Um einen zu erstellen, rufen Sie prepare () in dem Thread, der die Schleife ausführen soll, und dann Schleife () an lassen Sie Meldungen verarbeiten, bis die Schleife gestoppt ist.
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 ermöglicht Ihnen die asynchrone Arbeit an Ihrem Benutzer Schnittstelle. Es führt die Blockierungsvorgänge in einem Arbeitsthread und .__ aus. veröffentlicht dann die Ergebnisse im UI-Thread, ohne dass Sie Handle Threads und/oder Handler selbst.
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
}
}
Mit einem Handler können Sie Message- und Runable-Objekte senden und verarbeiten der MessageQueue eines Threads zugeordnet ist.
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;
}
});
Versuchen Sie dies, wenn Sie RuntimeException aufgrund von Looper sehen, der nicht vor dem Handler vorbereitet wurde.
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
@override
void run() {
// Run your task here
}
}, 1000 );
Toast.makeText()
sollte vom Main/UI-Thread aufgerufen werden. Looper.getMainLooper () hilft Ihnen dabei:
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Toast toast = Toast.makeText(mContext, "Something", Toast.LENGTH_SHORT);
}
});
Ein Vorteil dieser Methode ist, dass Sie sie auch in Klassen ohne Aktivität (oder ohne Kontext) verwenden können.
Ich bin auf das gleiche Problem gestoßen und hier ist es, wie ich es behoben habe:
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);
}
Um den UIHandler zu erstellen, müssen Sie Folgendes ausführen:
HandlerThread uiThread = new HandlerThread("UIHandler");
uiThread.start();
uiHandler = new UIHandler((HandlerThread) uiThread.getLooper());
Hoffe das hilft.
Grund für einen Fehler:
Worker-Threads sind für Hintergrundaufgaben gedacht, und Sie können auf der Benutzeroberfläche innerhalb eines Worker-Threads nichts anzeigen, wenn Sie keine Methode wie runOnUiThread aufrufen. Wenn Sie versuchen, im UI-Thread etwas anzuzeigen, ohne runOnUiThread aufzurufen, wird ein Java.lang.RuntimeException
angezeigt.
Wenn Sie sich in einer activity
befinden, aber Toast.makeText()
vom Worker-Thread aus aufrufen, führen Sie Folgendes aus:
runOnUiThread(new Runnable()
{
public void run()
{
Toast toast = Toast.makeText(getApplicationContext(), "Something", Toast.LENGTH_SHORT).show();
}
});
Der obige Code stellt sicher, dass Sie die Toast-Nachricht in einem UI thread
anzeigen, da Sie sie in der runOnUiThread
-Methode aufrufen. Also kein Java.lang.RuntimeException
mehr.
Ich habe diesen Fehler erhalten, bis ich Folgendes tat.
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();
}
}
);
}
Und machte dies zu einer Einzelklasse:
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();
}
}
);
}
}
das ist, was ich tat.
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Toast(...);
}
});
Visuelle Komponenten sind für Änderungen von externen Threads "gesperrt". Da der Toast auf dem Hauptbildschirm, der vom Haupt-Thread verwaltet wird, angezeigt wird, müssen Sie diesen Code in diesem Thread ausführen :)
Dies liegt daran, dass Toast.makeText () von einem Arbeitsthread aus aufgerufen wird. Es sollte so vom Haupt-UI-Thread aufgerufen werden
runOnUiThread(new Runnable() {
public void run() {
Toast toast = Toast.makeText(mContext, "Something", Toast.LENGTH_SHORT);
}
});
Die Antwort von ChicoBird funktionierte für mich. Die einzige Änderung, die ich vorgenommen habe, war die Erstellung des UI-Handlers, wo ich etwas tun musste
HandlerThread uiThread = new HandlerThread("UIHandler");
Eclipse weigerte sich, etwas anderes zu akzeptieren. Macht Sinn, denke ich.
Auch uiHandler
ist eindeutig eine irgendwo definierte Klasse global. Ich behaupte immer noch nicht zu verstehen, wie Android dies tut und was los ist, aber ich bin froh, dass es funktioniert. Jetzt werde ich weiter studieren und sehen, ob ich verstehen kann, was Android macht und warum man all diese Reifen und Schleifen durchlaufen muss. Danke für die Hilfe ChicoBird.
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(mContext, "Message", Toast.LENGTH_SHORT).show();
}
});
Für Rxjava- und RxAndroid-Benutzer:
public static void shortToast(String msg) {
Observable.just(msg)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(message -> {
Toast.makeText(App.getInstance(), message, Toast.LENGTH_SHORT).show();
});
}
Ich hatte das gleiche Problem, als meine Callbacks versuchten, ein Dialogfeld anzuzeigen.
Ich habe es mit dedizierten Methoden in der Activity - auf der Activity Instanz Member Level - gelöst, die runOnUiThread(..)
verwenden
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();
}
});
}
Wunderbare Kotlin-Lösung:
runOnUiThread {
// Add your ui thread code here
}
Um ein Dialogfeld oder einen Toaster in einem Thread anzuzeigen, verwenden Sie am besten das Aktivitätsobjekt.
Zum Beispiel:
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 muss auf dem UI-Thread ausgeführt werden. Sie können Asynctask verwenden, um sie in der Android-Entwicklung ordnungsgemäß zu verwenden. In einigen Fällen müssen wir jedoch die Timeouts anpassen. Daher verwenden wir Threads. In Threads können wir jedoch keine Toast-Alertdialoge verwenden, wie wir sie in AsyncTask verwenden. Deshalb benötigen wir separate Handler, um diese aufzurufen.
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 obigem Beispiel möchte ich meinen Thread in 3 Sekunden schlafen und nachdem ich eine Toast-Nachricht anzeigen möchte, wird dies in Ihrem mainthread -Implementhandler ausgeführt.
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);
}
};
Ich habe hier switch case verwendet, denn wenn Sie unterschiedliche Meldungen auf dieselbe Weise anzeigen müssen, können Sie switch case in der Handler-Klasse verwenden ... Ich hoffe, das hilft Ihnen
Dies geschieht normalerweise, wenn im Hauptthread etwas von einem Hintergrundthread aus aufgerufen wird. Schauen wir uns zum Beispiel ein Beispiel an.
private class MyTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
textView.setText("Any Text");
return null;
}
}
Im obigen Beispiel setzen wir Text in der Textansicht, die sich im Haupt-UI-Thread der doInBackground () -Methode befindet, die nur für einen Worker-Thread ausgeführt wird.
Handler handler2;
Thread second_thread=new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
handler2=new Handler();
}
});
second_thread.start();
Handler2 verwendet jetzt wahrscheinlich einen anderen Thread, um die Nachrichten als den Haupt-Thread zu behandeln.
rufen Sie zuerst Looper.prepare()
und dann Toast.makeText().show()
auf. Letzter Aufruf Looper.loop()
wie:
Looper.prepare() // to be able to make toast
Toast.makeText(context, "not connected", Toast.LENGTH_LONG).show()
Looper.loop()
Ich hatte das gleiche Problem und habe es einfach behoben, indem ich den Toast in die onPostExecute () - Überschreibungsfunktion der Asynctask <> setzte und es funktionierte.