web-dev-qa-db-fra.com

Impossible de créer un gestionnaire dans un thread qui n'a pas appelé Looper.prepare ()

Que signifie l'exception suivante? Comment puis-je le réparer?

C'est le code:

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

C'est l'exception:

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)
830
michael

Vous l'appelez à partir d'un thread de travail. Vous devez appeler Toast.makeText() (et la plupart des autres fonctions traitant de l'interface utilisateur) à partir du thread principal. Vous pouvez utiliser un gestionnaire, par exemple.

Recherchez Communication avec le fil de l'interface utilisateur dans la documentation. En un mot:

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

Autres options:

Vous pouvez utiliser un AsyncTask , qui fonctionne bien pour la plupart des tâches exécutées en arrière-plan. Il y a des crochets que vous pouvez appeler pour indiquer la progression et quand c'est fait.

Vous pouvez également utiliser Activity.runOnUiThread () .

596
EboMike

Vous devez appeler Toast.makeText(...) à partir du thread d'interface utilisateur:

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

Ceci est copié-collé à partir de une autre (copie) SO réponse .

786
Jacob Marble

MISE À JOUR - 2016

La meilleure alternative consiste à utiliser RxAndroid (liaisons spécifiques pour RxJava) pour P dans MVP afin de prendre en charge les données.

Commencez par renvoyer Observable à partir de votre méthode existante.

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

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

Utilisez cet observable comme ceci -

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

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

Je sais que je suis un peu en retard, mais voilà . Android fonctionne essentiellement sur deux types de threads, à savoir UI thread et background thread . Selon la documentation Android - 

N'accédez pas à la boîte à outils de l'interface utilisateur Android en dehors du fil de l'interface utilisateur pour résoudre ce problème. Android propose plusieurs manières d'accéder au fil de l'interface utilisateur à partir d'autres threads. Voici une liste de méthodes pouvant vous aider:

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

Il existe maintenant différentes méthodes pour résoudre ce problème. 

Je vais l'expliquer par exemple de code:

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

BOUCLE

Classe utilisée pour exécuter une boucle de message pour un thread. Les threads par défaut font ne pas être associé à une boucle de message; pour en créer un, appelez prepare () dans le thread qui doit exécuter la boucle, puis loop () à faites-le traiter les messages jusqu'à ce que la boucle soit arrêtée.

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 vous permet d'effectuer un travail asynchrone sur votre utilisateur interface. Il effectue les opérations de blocage dans un thread de travail et publie ensuite les résultats sur le fil de l'interface utilisateur, sans vous obliger à manipulez vous-même les fils et/ou les manipulateurs.

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

Gestionnaire

Un gestionnaire vous permet d’envoyer et de traiter des objets Message et Runnable associé à la file MessageQueue.

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

Essayez ceci quand vous voyez runtimeException car Looper n’a pas été préparé avant le gestionnaire.

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

handler.postDelayed(new Runnable() {
  @override
  void run() {
  // Run your task here
  }
}, 1000 );
76
Khulja Sim Sim

Toast.makeText() doit être appelé à partir du thread principal/de l'interface utilisateur. Looper.getMainLooper () vous aide à y parvenir:

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

L'un des avantages de cette méthode est que vous pouvez également l'utiliser dans des classes non actives (ou sans contexte).

57
Sa Qada

J'ai rencontré le même problème et voici comment je l'ai corrigé:

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

Pour créer UIHandler, vous devez effectuer les opérations suivantes:

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

J'espère que cela t'aides.

39
ChicoBird

Raison de l'erreur:

Les threads de travail sont conçus pour effectuer des tâches en arrière-plan et vous ne pouvez rien afficher sur l'interface utilisateur d'un thread de travail à moins d'appeler une méthode telle que runOnUiThread . Si vous essayez d’afficher quoi que ce soit sur le fil de l’interface utilisateur sans appeler runOnUiThread, il y aura un Java.lang.RuntimeException.

Ainsi, si vous êtes dans une activity mais appelez Toast.makeText() à partir du thread de travail, procédez comme suit:

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

Le code ci-dessus garantit que vous affichez le message Toast dans un UI thread puisque vous l'appelez dans la méthode runOnUiThread. Donc, pas plus Java.lang.RuntimeException.

32
My God

Je recevais cette erreur jusqu'à ce que je fasse ce qui suit.

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

Et en ont fait une classe 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();
                }
            }
        );
    }

}
21
EpicPandaForce

c'est ce que j'ai fait.

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

Les composants visuels sont "verrouillés" aux modifications des threads externes . Ainsi, comme le pain grillé montre des éléments de l'écran principal géré par le thread principal, vous devez exécuter ce code sur ce thread . :)

19
eiran

En effet, Toast.makeText () appelle à partir d'un thread de travail. Il devrait être appelé depuis le fil principal de l'interface utilisateur comme ceci 

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

La réponse de ChicoBird a fonctionné pour moi. Le seul changement que j'ai apporté a été la création de UIHandler où je devais faire 

HandlerThread uiThread = new HandlerThread("UIHandler");

Eclipse a refusé d'accepter quoi que ce soit d'autre. Cela a du sens, je suppose.

De plus, uiHandler est clairement une classe globale définie quelque part. Je ne prétends toujours pas comprendre comment Android fait cela et ce qui se passe, mais je suis heureux que cela fonctionne. Maintenant, je vais l’étudier et voir si je peux comprendre ce que fait Android et pourquoi il faut passer par tous ces cercles et boucles. Merci pour l'aide ChicoBird.

7
Brian Reinhold
 runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(mContext, "Message", Toast.LENGTH_SHORT).show();
            }
        });
5
Coldfin Lab

Pour l'utilisateur Rxjava et RxAndroid:

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

Je rencontrais le même problème lorsque mes rappels essayaient d'afficher un dialogue. 

Je l'ai résolu avec des méthodes dédiées dans l'activité - au niveau du membre d'activité instance - qui utilisent 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();
        }
    });
}
4
Gene Bo

Merveilleuse solution de Kotlin:

runOnUiThread {
    // Add your ui thread code here
}
1
jungledev

Pour afficher une boîte de dialogue ou un grille-pain dans un fil, le moyen le plus concis est d'utiliser l'objet Activité.

Par exemple:

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

Toast, AlertDialogs doit s'exécuter sur un thread d'interface utilisateur. Vous pouvez utiliser Asynctask pour les utiliser correctement dans le développement Android. Cependant, dans certains cas, nous devons personnaliser les délais d'expiration afin d'utiliser Threads, mais dans les threads, nous ne pouvons pas utiliser Toast, Alertdialogs comme dans AsyncTask. Nous avons donc besoin de Handler séparé pour les afficher en incrustation.

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

dans l'exemple ci-dessus, je veux mettre mon thread en veille en 3sec et après, je veux afficher un message Toast, pour cela dans votre gestionnaire d'implémentation mainthread.

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

J'ai utilisé le cas de commutateur ici, parce que si vous avez besoin d'afficher un message différent de la même manière, vous pouvez utiliser le cas de commutateur dans la classe Handler ... espérons que cela vous aidera

0
Ashana.Jackol

Cela se produit généralement lorsque quelque chose sur le thread principal est appelé depuis n'importe quel thread d'arrière-plan. Regardons un exemple, par exemple.

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


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

Dans l'exemple ci-dessus, nous définissons du texte dans la vue de texte qui se trouve dans le thread d'interface utilisateur principal à partir de la méthode doInBackground (), qui fonctionne uniquement sur un thread de travail.

0
Prashant Paliwal
Handler handler2;  
Thread second_thread=new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                handler2=new Handler();
            }
        });
        second_thread.start();

Maintenant, handler2 utilisera probablement un autre thread pour gérer les messages que le thread principal.

0
Tarasantan

premier appel Looper.prepare() puis appelez Toast.makeText().show() dernier appel Looper.loop() comme:

Looper.prepare() // to be able to make toast
Toast.makeText(context, "not connected", Toast.LENGTH_LONG).show()
Looper.loop()
0
Hasan A Yousef

J'ai eu le même problème et je l'ai corrigé simplement en plaçant le Toast dans la fonction de substitution onPostExecute () de l'Asynctask <> et cela a fonctionné. 

0
alibabaei12