No se puede crear un controlador dentro del hilo que no haya llamado Looper.prepare ()

983

¿Qué significa la siguiente excepción? ¿Cómo puedo arreglarlo?

Este es el código:

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

Esta es la excepción:

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)
Miguel
fuente
8
revise esta biblioteca compile 'com.shamanland:xdroid-toaster:0.0.5', no requiere runOnUiThread()o Contextvariable, ¡toda la rutina se ha ido! solo invoque Toaster.toast(R.string.my_msg);aquí es el ejemplo: github.com/shamanland/xdroid-toaster-example
Oleksii K.
120
¡Qué mensaje de error tan estúpido! Podría haber sido tan simple como: no se puede llamar a esto desde un subproceso sin interfaz de usuario como se hace cuando las vistas se tocan desde un subproceso sin interfaz de usuario.
Dheeraj Bhaskar
11
Para aquellos que reciben el mismo mensaje de excepción de un código diferente: lo que significa el mensaje de excepción es que está llamando al código a través de un hilo que no ha preparado Looper. Normalmente significa que no está llamando desde el subproceso de la interfaz de usuario, pero debería (caso de OP): un subproceso normal no prepara a Looper, pero el subproceso de la interfaz de usuario siempre lo hace.
Helin Wang
@OleksiiKropachov, la implementación de la biblioteca que mencionaste es muy similar a hacer un runOnUiThread ().
Helin Wang
Sí, pero es un envoltorio muy útil
Oleksii K.

Respuestas:

697

Lo estás llamando desde un hilo de trabajo. Debe llamar Toast.makeText()(y la mayoría de las otras funciones relacionadas con la IU) desde el hilo principal. Podría usar un controlador, por ejemplo.

Consulte Comunicación con el subproceso de interfaz de usuario en la documentación. En una palabra:

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

Otras opciones:

Puede usar una AsyncTask , que funciona bien para la mayoría de las cosas que se ejecutan en segundo plano. Tiene ganchos a los que puede llamar para indicar el progreso y cuándo está hecho.

También puede usar Activity.runOnUiThread () .

EboMike
fuente
¿Qué pasa con el problema original (no se trataba de AlertDialog)?
Ivan G.
55
Solo agrego mis dos centavos a lo que dijo Cleggy. Sería preferible proporcionar una breve demostración de lo que quiere decir (aunque sea artificial), ya que un ejemplo codificado a menudo puede decir mucho por sí mismo.
cdata
55
para obtener una respuesta técnica completa, consulte este prasanta-paul.blogspot.kr/2013/09/…
tony9099
3
En casi todos los lenguajes de programación AFAIK que admiten la GUI, si actualiza / cambia / muestra / interactúa con la GUI directamente, debe hacerse en el hilo principal del programa.
Ahmed
(and most other functions dealing with the UI)Un ejemplo de una función de interfaz de usuario que se puede usar desde el fondo es android.support.design.widget.Snackbar: su funcionalidad no se ve mermada cuando no se llama desde el hilo de la interfaz de usuario.
Desaliñado
854

Debe llamar Toast.makeText(...)desde el hilo de la interfaz de usuario:

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

Esto se copia de otra respuesta SO (duplicada) .

Jacob Marble
fuente
13
Gran respuesta. Esto me tuvo confundido por un tiempo. Solo para tener en cuenta, no necesitaba la actividad. antes de runOnUiThread.
Cen92
448

ACTUALIZACIÓN - 2016

La mejor alternativa es utilizar RxAndroid(fijaciones específicas para RxJava) para el Pde MVPhacerse cargo fo datos.

Comience por regresar Observablede su método existente.

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

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

Use este Observable como este:

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

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

Sé que llego un poco tarde pero aquí va. Básicamente, Android funciona en dos tipos de subprocesos, a saber, subproceso de interfaz de usuario y subproceso de fondo . De acuerdo con la documentación de Android:

No acceda al kit de herramientas de la interfaz de usuario de Android desde fuera del hilo de la interfaz de usuario para solucionar este problema, Android ofrece varias formas de acceder al hilo de la interfaz de usuario desde otros hilos. Aquí hay una lista de métodos que pueden ayudar:

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

Ahora hay varios métodos para resolver este problema.

Lo explicaré por ejemplo de código:

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

LOOPER

Clase utilizada para ejecutar un bucle de mensaje para un hilo. Los subprocesos por defecto no tienen un bucle de mensajes asociado con ellos; para crear uno, llame a prepare () en el hilo que va a ejecutar el bucle, y luego bucle () para que procese los mensajes hasta que se detenga.

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 le permite realizar trabajos asincrónicos en su interfaz de usuario. Realiza las operaciones de bloqueo en un subproceso de trabajo y luego publica los resultados en el subproceso de la interfaz de usuario, sin necesidad de que usted mismo maneje los subprocesos y / o controladores.

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

Manipulador

Un controlador le permite enviar y procesar mensajes y objetos ejecutables asociados con MessageQueue de un hilo.

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;
    }
});
mjosh
fuente
77
Esto es exactamente lo que estaba buscando. Especialmente el primer ejemplo conrunOnUiThread
Navin
55
Gracias, 5 años de programación en Android y nunca supe que Viewtambién tiene métodos post(Runnable)y postDelayed(Runnable, long)! Tantos manipuladores en vano. :)
Fenix ​​Voltres
para aquellos que están confundidos por el ejemplo de Handler: ¿cuál es el hilo al que se une "new Handler (callback)"? Está vinculado al hilo que creó el controlador.
Helin Wang
1
¿Por qué es esta la mejor alternativa ?
IgorGanapolsky
Usw doInBackground y quiero recuperar una ArrayList pero siempre recibo el error: No se puede crear un controlador dentro del hilo que no haya llamado Looper.prepare (). Vea esta es mi pregunta stackoverflow.com/questions/45562615/… pero no puedo obtener la solución de esta respuesta aquí
WeSt
120

Toast.makeText()solo debe llamarse desde el subproceso principal / UI. Looper.getMainLooper () te ayuda a lograrlo:

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

Una ventaja de este método es que puede usarlo sin Actividad o Contexto.

Ayaz Alifov
fuente
2
Gracias, las otras respuestas no me funcionaron. Estoy usando un registro de azúcar de la biblioteca para gestionar la persistencia. Y dentro no tengo la actividad. Pero esto funciona de maravilla
cabaji99
91

Intente esto, cuando vea runtimeException debido a que Looper no está preparado antes del controlador.

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

handler.postDelayed(new Runnable() {
  @Override
  public void run() {
  // Run your task here
  }
}, 1000 );
Khulja Sim Sim
fuente
Handler es una clase abstracta. esto no compila
Stealth Rabbi
2
@StealthRabbi Import Handler desde el espacio de nombres correcto, es decirandroid.os.Handler
NightFury
Este puede no ser el problema. Es posible que no exista un bucle de la clase que llama, punto.
IgorGanapolsky
42

Me encontré con el mismo problema, y ​​así es como lo solucioné:

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

Para crear el UIHandler, deberá realizar lo siguiente:

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

Espero que esto ayude.

ChicoBird
fuente
Traté de usar su código pero perdí y no estoy seguro de cómo llamar desde onCreate methodo desde AsyncTask en mi situación, ¿podría publicar todo el código solo para saber cómo funcionan las cosas?
Nick Kahn
2
¿No debería leer esa línea final uiHandler = new UIHandler(uiThread.getLooper()); ?
Beer Me
36

Motivo de un error:

Los subprocesos de trabajo están destinados a realizar tareas en segundo plano y no puede mostrar nada en la interfaz de usuario dentro de un subproceso de trabajo a menos que llame a un método como runOnUiThread . Si intenta mostrar algo en el hilo de la interfaz de usuario sin llamar a runOnUiThread, habrá un java.lang.RuntimeException.

Entonces, si está en un hilo de trabajador activitypero llamando Toast.makeText(), haga esto:

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

El código anterior asegura que está mostrando el mensaje Toast en un método UI threadya que lo está llamando dentro runOnUiThread. Entonces no más java.lang.RuntimeException.

Dios mío
fuente
23

eso fue lo que hice.

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

Los componentes visuales están "bloqueados" a los cambios desde hilos externos. Entonces, dado que el brindis muestra cosas en la pantalla principal administradas por el hilo principal, debe ejecutar este código en ese hilo. Espero que ayude:)

eiran
fuente
Usé este mismo método. Sin embargo, ¿deja esto abierto la posibilidad de fugas, porque la clase interna anónima del Runnable tendrá una referencia implícita a la Actividad?
Peter G. Williams
1
Ese es un buen punto :) solo use getApplicationContext () o algo así, para estar seguro. aunque nunca tuve problemas con ese código que conozco
eiran
22

Recibí este error hasta que hice lo siguiente.

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

Y convirtió esto en una clase singleton:

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

}
EpicPandaForce
fuente
¿Dónde estás usando la tostadora ? En tu primer fragmento no se usa ...
IgorGanapolsky
1
fue una clase de conveniencia que usé como Toaster.INSTANCE.postMessage(ResourceUtils.getString(R.string.blah));(¡lo sé, lo reducimos más tarde!), aunque no he estado usando tostadas en mucho tiempo
EpicPandaForce
Entonces, ¿qué ApplicationHolder.INSTANCEevalúa?
IgorGanapolsky
Una variable estática de CustomApplicationCustomApplication.onCreate()
inicio
12
 runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(mContext, "Message", Toast.LENGTH_SHORT).show();
            }
        });
Laboratorio Coldfin
fuente
2
Esto funcionó para mí y uso lambdarunOnUiThread(() -> { Toast toast = Toast.makeText(getApplicationContext(), "Message", Toast.LENGTH_SHORT); toast.show(); });
Black_Zerg
Gracias. Funcionó para mí
Amin
11

Maravillosa solución de Kotlin:

runOnUiThread {
    // Add your ui thread code here
}
jungledev
fuente
44
runOnUiThreades parte de la actividad, es deciractivity?.runOnUiThread { ... }
AtomicStrongForce
9

Esto se debe a que Toast.makeText () está llamando desde un subproceso de trabajo. Debe llamarse desde el hilo principal de la interfaz de usuario como este

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

La respuesta de ChicoBird funcionó para mí. El único cambio que hice fue en la creación del UIHandler donde tuve que hacer

HandlerThread uiThread = new HandlerThread("UIHandler");

Eclipse se negó a aceptar cualquier otra cosa. Tiene sentido, supongo.

Además, uiHandleres claramente una clase global definida en alguna parte. Todavía no pretendo entender cómo Android está haciendo esto y qué está sucediendo, pero me alegro de que funcione. Ahora procederé a estudiarlo y veré si puedo entender qué está haciendo Android y por qué uno tiene que pasar por todos estos aros y bucles. Gracias por la ayuda ChicoBird.

Brian Reinhold
fuente
6

primera llamada Looper.prepare()y luego llamada Toast.makeText().show()última llamada Looper.loop()como:

Looper.prepare() // to be able to make toast
Toast.makeText(context, "not connected", Toast.LENGTH_LONG).show()
Looper.loop()
Hasan A Yousef
fuente
¿Por qué esta respuesta está subestimada?
DkPathak
5

Para usuarios de Rxjava y RxAndroid:

public static void shortToast(String msg) {
    Observable.just(msg)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(message -> {
                Toast.makeText(App.getInstance(), message, Toast.LENGTH_SHORT).show();
            });
}
Geng Jiawen
fuente
Esos corchetes son innecesarios
Borja
4

Me encontraba con el mismo problema cuando mis devoluciones de llamada intentaban mostrar un diálogo.

Lo resolví con métodos dedicados en la Actividad, en el nivel de miembro de la instancia de Actividad , que usanrunOnUiThread(..)

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();
        }
    });
}
Gene Bo
fuente
2
Handler handler2;  
HandlerThread handlerThread=new HandlerThread("second_thread");
handlerThread.start();
handler2=new Handler(handlerThread.getLooper());

Ahora handler2 usará un hilo diferente para manejar los mensajes que el hilo principal.

Tarasantan
fuente
1

Para mostrar un cuadro de diálogo o una tostadora en un hilo, la forma más concisa es usar el objeto Actividad.

Por ejemplo:

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()
...
Liwen Zhao
fuente
0

Toast, AlertDialogs necesita ejecutarse en el hilo de la interfaz de usuario, puede usar Asynctask para usarlos correctamente en el desarrollo de Android. Pero en algunos casos necesitamos personalizar los tiempos de espera, por lo que usamos hilos , pero en los hilos no podemos usar Toast, Alertdialogs como usamos en AsyncTask.So que necesita separar Handler para aquellos emergente.

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

en el ejemplo anterior, quiero dormir mi hilo en 3 segundos y después quiero mostrar un mensaje Toast, para eso en su controlador de implementación de hilo principal .

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

Usé el caso de cambio aquí, porque si necesita mostrar un mensaje diferente de la misma manera, puede usar el caso de cambio dentro de la clase Handler ... espero que esto lo ayude

Ashana.Jackol
fuente
0

Esto generalmente ocurre cuando se llama a algo en el hilo principal desde cualquier hilo de fondo. Veamos un ejemplo, por ejemplo.

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


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

En el ejemplo anterior, estamos configurando texto en la vista de texto que se encuentra en el hilo principal de la interfaz de usuario del método doInBackground (), que opera solo en un hilo de trabajo.

Prashant Paliwal
fuente
0

Tuve el mismo problema y lo solucioné simplemente poniendo Toast en la función de anulación onPostExecute () de Asynctask <> y funcionó.

alibabaei12
fuente
-2

uso el siguiente código para mostrar el mensaje del "contexto" del hilo principal,

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

luego use como lo siguiente:

class myClass implements IShowMessage{

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