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

81

Tengo una Actividad, y en eso tengo una clase.

text=new Dynamictext(...);
text.setText("txt");

en mi DynamicText java tengo este código:

public void setText(String text) {
    this.text=text;
    new asyncCreateText().execute();
    //this.createText(text);
}

//private Handler handler = new Handler();

private class asyncCreateText extends AsyncTask<Void, Void, Void> 
{
    @Override
    protected Void doInBackground(Void... unused) {
        return null;
    }

    @Override
    protected void onPostExecute(Void unused) {

    }
}

Yo obtengo:

ERROR / AndroidRuntime (5176): Causado por: java.lang.RuntimeException: No se puede crear un controlador dentro del hilo que no ha llamado a Looper.prepare ()

¿Cómo puedo manejar este error?

ERROR/AndroidRuntime(5370): java.lang.ExceptionInInitializerError
ERROR/AndroidRuntime(5370):     at com.l.start.DynamicText.setText(DynamicText.java:125)
ERROR/AndroidRuntime(5370):     at com.l.start.OpenGLRenderer.initfonts(OpenGLRenderer.java:168)
ERROR/AndroidRuntime(5370):     at com.l.start.OpenGLRenderer.init(OpenGLRenderer.java:119)
ERROR/AndroidRuntime(5370):     at com.l.start.OpenGLRenderer.onSurfaceChanged(OpenGLRenderer.java:90)
ERROR/AndroidRuntime(5370):     at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1120)
ERROR/AndroidRuntime(5370):     at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:975)

ERROR/AndroidRuntime(5370): Caused by: java.lang.RuntimeException: 
    Can't create handler inside thread that has not called Looper.prepare()
ERROR/AndroidRuntime(5370):     at android.os.Handler.<init>(Handler.java:121)
ERROR/AndroidRuntime(5370):     at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421)
ERROR/AndroidRuntime(5370):     at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421)
ERROR/AndroidRuntime(5370):     at android.os.AsyncTask.<clinit>(AsyncTask.java:152)
ERROR/AndroidRuntime(5370):     ... 6 more
lacas
fuente
¿Puede publicar el seguimiento de la pila completa? Parece que está llamando a algún método de interfaz de usuario desde el hilo de fondo o algo así.
alexanderblom
1
Esto explicará el problema en detalle.
mjosh

Respuestas:

146

El error se explica por sí mismo ... se doInBackground()ejecuta en un subproceso en segundo plano que, dado que no está destinado a repetirse, no está conectado a un archivo Looper.

Lo más probable es que no desee crear directamente una instancia de un controlador ... los datos que doInBackground()devuelva su implementación se pasarán a los onPostExecute()que se ejecutan en el hilo de la interfaz de usuario.

   mActivity = ThisActivity.this; 

    mActivity.runOnUiThread(new Runnable() {
     public void run() {
     new asyncCreateText().execute();
     }
   });

AÑADIDO SIGUIENDO EL STACKTRACE QUE APARECE EN PREGUNTA:

Parece que estás intentando iniciar un AsyncTaskhilo de renderizado GL ... no hagas eso porque ellos tampoco lo harán nunca Looper.loop(). AsyncTasks están realmente diseñadas para ejecutarse solo desde el hilo de la interfaz de usuario.

La solución menos perjudicial sería probablemente para llamar Activity.runOnUiThread()con una Runnableque da inicio a su AsyncTask.

Reuben Scratton
fuente
1
La respuesta es totalmente correcta. Sin embargo, en mi caso, solo tuve que eliminar algunos Toasts (intentando ejecutar en el hilo de la interfaz de usuario) de mi clase personalizada FtpUpload AsyncTask y listo.
Josh
Me viene el mismo error. Estoy confuso sobre cómo resolverlo. Leí la descripción, pero no entiendo cómo hacerlo. ¿Alguien puede darme un fragmento de código? Por favor
Android lo es todo para mí
32

Todas las respuestas anteriores son correctas, pero creo que este es el ejemplo más fácil posible:

public class ExampleActivity extends Activity {
    private Handler handler;
    private ProgressBar progress;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        progress = (ProgressBar) findViewById(R.id.progressBar1);
        handler = new Handler();
    }

    public void clickAButton(View view) {
        // Do something that takes a while
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                handler.post(new Runnable() { // This thread runs in the UI
                    @Override
                    public void run() {
                        progress.setProgress("anything"); // Update the UI
                    }
                });
            }
        };
        new Thread(runnable).start();
    }
}

Lo que hace esto es actualizar una barra de progreso en el hilo de la interfaz de usuario desde un hilo completamente diferente pasado a través del método post () del controlador declarado en la actividad.

¡Espero eso ayude!

Lisandro
fuente
22

Creas un controlador en un hilo de fondo de esta manera

private void createHandler() {
        Thread thread = new Thread() {
          public void run() {
               Looper.prepare();

               final Handler handler = new Handler();
               handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                       // Do Work
                        handler.removeCallbacks(this);
                        Looper.myLooper().quit();
                   }
                }, 2000);

                Looper.loop();
            }
        };
        thread.start();
    }
Ayman Mahgoub
fuente
No solo resuelve el problema, sino que también es muy explicativo. Un poco de masa problemática. Cómo cambiará la estructura si así lo desea removeCallbacksy quitcuándo Workse hace, pero no se alcanza el retraso Porque el trabajo puede durar más que el retraso predefinido.
Farid
12

Activity.runOnUiThread()no funciona para mi Trabajé alrededor de este problema creando un hilo regular de esta manera:

public class PullTasksThread extends Thread {
   public void run () {
      Log.d(Prefs.TAG, "Thread run...");
   }
}

y llamarlo desde la actualización GL de esta manera:

new PullTasksThread().start();
Eli
fuente
¡Esta solución funcionó para mí llamándola desde GL, pero debe asegurarse de no llamar al método Toasts on run! AsyncTask no funcionó al llamarlo desde GL. ¡Gracias!
Alberto M.
Funciona muy bien para mí reemplazando una AsyncTask. ¡Gracias!
IgorGanapolsky
5

Intenta ejecutar tu asyntask desde el hilo de la interfaz de usuario. ¡Enfrenté este problema cuando no estaba haciendo lo mismo!

usuario2560131
fuente
3

Prueba esto

    Handler mHandler = new Handler(Looper.getMainLooper());
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
 //your code here that talks with the UI level widgets/ try to access the UI 
//elements from this block because this piece of snippet will run in the UI/MainThread.                                     
                            }
                        });
Marcos Militão
fuente
1
Intente agregar una pequeña explicación. Las respuestas de solo código no son muy apreciadas en SO.
Alexei