Não é possível criar o manipulador dentro do thread que não chamou Looper.prepare ()

981

O que significa a seguinte exceção; como posso consertar isso?

Este é o código:

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

Esta é a exceção:

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)
Michael
fonte
8
verifique esta biblioteca compile 'com.shamanland:xdroid-toaster:0.0.5', não requer runOnUiThread()ou Contextvariável, toda a rotina se foi! apenas invocar Toaster.toast(R.string.my_msg);aqui está o exemplo: github.com/shamanland/xdroid-toaster-example
Oleksii K.
120
Que mensagem de erro estúpida! Poderia ter sido tão simples quanto - não é possível chamar isso de um thread que não seja da interface do usuário, como foi feito quando as visualizações são tocadas em um thread que não é da interface do usuário.
Dheeraj Bhaskar
11
Para quem recebe a mesma mensagem de exceção de código diferente: O que a mensagem de exceção significa é que você está chamando o código por meio de um encadeamento que não preparou o Looper. Normalmente, isso significa que você não está chamando se do thread da interface do usuário, mas deveria (caso do OP) - um thread normal não prepara o Looper, mas o thread da UI sempre o faz.
Helin Wang
@OleksiiKropachov, a implementação da biblioteca que você mencionou é muito semelhante a executar um runOnUiThread ().
Helin Wang
sim, mas é um invólucro muito útil
Oleksii K.

Respostas:

696

Você está chamando isso de um thread de trabalho. Você precisa chamar Toast.makeText()(e a maioria das outras funções que lidam com a interface do usuário) de dentro do thread principal. Você pode usar um manipulador, por exemplo.

Consulte Comunicação com o thread da interface do usuário na documentação. Em poucas palavras:

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

Outras opções:

Você pode usar um AsyncTask , que funciona bem para a maioria das coisas em execução em segundo plano. Tem ganchos que você pode chamar para indicar o progresso e quando terminar.

Você também pode usar Activity.runOnUiThread () .

EboMike
fonte
e o problema original (não se tratava de AlertDialog)?
Ivan G.
5
Apenas adicionando meus dois centavos ao que Cleggy disse. Seria preferível fornecer uma breve demonstração do que você quer dizer (por mais artificial que seja), pois um exemplo codificado pode frequentemente falar por si.
Cdata #
5
para obter uma resposta técnica completa, consulte prasanta-paul.blogspot.kr/2013/09/…
tony9099
3
Em quase todas as linguagens de programação AFAIK que oferecem suporte à GUI, se você atualizar / alterar / exibir / interagir diretamente com a GUI, isso deverá ser feito no thread principal do programa.
Ahmed
(and most other functions dealing with the UI)Um exemplo de uma função de interface do usuário utilizável em segundo plano é android.support.design.widget.Snackbar- sua funcionalidade é diminuída quando não é chamada a partir do thread da interface do usuário.
Scruffy
854

Você precisa chamar Toast.makeText(...)do thread da interface do usuário:

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

Isso é copiado e colado de outra resposta do SO (duplicada) .

Jacob Marble
fonte
13
Ótima resposta. Isso me deixou confuso por um tempo. Apenas para observar, eu não precisava da atividade. antes do runOnUiThread.
Cen92
448

ATUALIZAÇÃO - 2016

A melhor alternativa é usar RxAndroid(ligações específicas para RxJava) para o Pno MVPpara assumir o comando fo dados.

Comece retornando Observabledo seu método existente.

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

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

Use este Observável assim -

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

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

Eu sei que estou um pouco atrasado, mas aqui vai. O Android basicamente funciona em dois tipos de encadeamentos, ou seja, encadeamento da interface do usuário e encadeamento em segundo plano . De acordo com a documentação do Android -

Não acesse o kit de ferramentas da interface do usuário do Android de fora do thread da interface do usuário para corrigir esse problema, o Android oferece várias maneiras de acessar o thread da interface do usuário a partir de outros threads. Aqui está uma lista de métodos que podem ajudar:

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

Agora, existem vários métodos para resolver esse problema.

Vou explicar isso por exemplo 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

Classe usada para executar um loop de mensagem para um encadeamento. Os encadeamentos por padrão não têm um loop de mensagem associado a eles; para criar um, chame prepare () no thread que deve executar o loop e, em seguida, loop () para que ele processe as mensagens até que o loop seja interrompido.

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

O AsyncTask permite que você execute um trabalho assíncrono na interface do usuário. Ele executa as operações de bloqueio em um encadeamento de trabalho e, em seguida, publica os resultados no encadeamento da interface do usuário, sem exigir que você lide com os encadeamentos e / ou manipuladores.

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

Handler

Um manipulador permite enviar e processar objetos Message e Runnable associados ao MessageQueue de um thread.

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
fonte
7
Era exatamente isso que eu estava procurando. Especialmente o primeiro exemplo comrunOnUiThread
Navin
5
Obrigado, 5 anos de programação Android e eu nunca soube Viewtambém tem métodos post(Runnable)e postDelayed(Runnable, long)! Tantos manipuladores em vão. :)
Fenix ​​Voltres
para aqueles que estão confusos com o exemplo do manipulador: qual é o segmento ao qual "novo manipulador (retorno de chamada)" está vinculado? É vinculado ao segmento que criou o manipulador.
Helin Wang
1
Por que essa é a melhor alternativa ?
IgorGanapolsky 02/02
Eu uso doInBackground e quero recuperar um ArrayList, mas sempre recebo o erro: Não é possível criar o manipulador dentro do thread que não chamou Looper.prepare (). Veja esta é a minha pergunta stackoverflow.com/questions/45562615/… mas não consigo obter a solução desta resposta aqui
#
120

Toast.makeText()deve ser chamado apenas do thread Principal / UI. Looper.getMainLooper () ajuda você a alcançá-lo:

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

Uma vantagem desse método é que você pode usá-lo sem Atividade ou Contexto.

Ayaz Alifov
fonte
2
Obrigado, as outras respostas não estavam funcionando para mim. Estou usando um registro de açúcar da biblioteca para gerenciar a persistência. E por dentro eu não tenho a atividade. Mas isso funciona maravilhosamente
cabaji99
91

Tente isso quando vir runtimeException devido ao Looper não preparado antes do manipulador.

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

handler.postDelayed(new Runnable() {
  @Override
  public void run() {
  // Run your task here
  }
}, 1000 );
Khulja Sim Sim
fonte
Handler é uma classe abstrata. isso não é compilado
Stealth Rabbi
2
@StealthRabbi importação manipulador de espaço de nomes ou seja correctoandroid.os.Handler
NightFury
Este pode não ser o problema. Um looper pode não existir da classe de chamada, período.
IgorGanapolsky 2/02
42

Corri para o mesmo problema, e aqui está como corrigi-lo:

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 criar o UIHandler, você precisará executar o seguinte:

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

Espero que isto ajude.

ChicoBird
fonte
Eu tentei usar o seu código, mas perdi e não tenho certeza de como ligar de onCreate methodou do AsyncTask na minha situação. Você pode publicar o código inteiro apenas para saber como as coisas funcionam?
27612 Nick Kahn
2
Essa linha final não deveria ser lida uiHandler = new UIHandler(uiThread.getLooper()); ?
Beer Me
36

Razão para um erro:

Os encadeamentos de trabalho destinam-se à execução de tarefas em segundo plano e você não pode mostrar nada na interface do usuário em um encadeamento de trabalho, a menos que chame um método como runOnUiThread . Se você tentar mostrar algo no thread da interface do usuário sem chamar runOnUiThread, haverá umjava.lang.RuntimeException .

Portanto, se você estiver em uma activitychamada, mas Toast.makeText()do segmento de trabalho, faça o seguinte:

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

O código acima garante que você esteja mostrando a mensagem Toast em um método UI threaddesde que a está chamando dentro runOnUiThread. Então não mais java.lang.RuntimeException.

Meu Deus
fonte
23

Isso é o que eu fiz.

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

Os componentes visuais estão "bloqueados" para alterações de threads externos. Portanto, como o brinde mostra coisas na tela principal que são gerenciadas pelo thread principal, você precisa executar esse código nesse segmento. Espero que ajude:)

eiran
fonte
Eu usei esse mesmo método. No entanto, isso deixa em aberto a possibilidade de vazamentos, porque a classe interna anônima do Runnable manterá uma referência implícita à Atividade?
Peter G. Williams
1
Esse é um ponto interessante :) basta usar getApplicationContext () ou algo parecido, para estar no lado seguro. embora eu nunca tive problemas com esse código que eu conheço
eiran
22

Eu estava recebendo esse erro até fazer o seguinte.

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

E transformou isso em uma 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();
                }
            }
        );
    }

}
EpicPandaForce
fonte
Onde você está usando a Torradeira ? No seu primeiro trecho, ele não é usado ...
IgorGanapolsky
1
era uma classe de conveniência que eu usei como Toaster.INSTANCE.postMessage(ResourceUtils.getString(R.string.blah));(longa Sei que reduziu esta tarde!), embora eu não tenha vindo a utilizar brindes em quando
EpicPandaForce
Então, o que ApplicationHolder.INSTANCEavaliar para?
IgorGanapolsky
Uma variável estática de CustomApplicationconjunto em CustomApplication.onCreate(), considerando-se a aplicação sempre existe, enquanto existe o processo, neste contexto, pode ser utilizada a nível mundial
EpicPandaForce
12
 runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(mContext, "Message", Toast.LENGTH_SHORT).show();
            }
        });
Coldfin Lab
fonte
2
Isso funcionou para mim e eu uso lambdarunOnUiThread(() -> { Toast toast = Toast.makeText(getApplicationContext(), "Message", Toast.LENGTH_SHORT); toast.show(); });
Black_Zerg
Obrigado. Funcionou para mim
Amin
11

Maravilhosa solução Kotlin:

runOnUiThread {
    // Add your ui thread code here
}
jungledev
fonte
4
runOnUiThreadfaz parte da atividade, ou seja,activity?.runOnUiThread { ... }
AtomicStrongForce
9

Isso ocorre porque Toast.makeText () está chamando de um thread de trabalho. Deve ser chamada do thread principal da interface do usuário como este

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

A resposta de ChicoBird funcionou para mim. A única alteração que fiz foi na criação do UIHandler, onde eu tive que fazer

HandlerThread uiThread = new HandlerThread("UIHandler");

Eclipse se recusou a aceitar qualquer outra coisa. Faz sentido, suponho.

Também uiHandleré claramente uma classe global definida em algum lugar. Ainda não pretendo entender como o Android está fazendo isso e o que está acontecendo, mas estou feliz que funcione. Agora vou estudá-lo e ver se consigo entender o que o Android está fazendo e por que é preciso passar por todos esses ciclos. Obrigado pela ajuda ChicoBird.

Brian Reinhold
fonte
6

primeira Looper.prepare()e depois a Toast.makeText().show()última, 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
fonte
Por que essa resposta é subestimada?
DkPathak
5

Para usuários Rxjava e 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
fonte
Esses suportes são desnecessários
Borja
4

Eu estava com o mesmo problema quando meus retornos de chamada tentavam mostrar uma caixa de diálogo.

Eu o resolvi com métodos dedicados na Activity - no nível de membro da instância Activity - que usamrunOnUiThread(..)

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

Agora handler2 usará um Thread diferente para manipular as mensagens que o Thread principal.

Tarasantan
fonte
1

Para exibir uma caixa de diálogo ou uma torradeira em um thread, a maneira mais concisa é usar o objeto Activity.

Por exemplo:

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
fonte
0

Brinde, AlertDialogs precisa ser executado no segmento interface do usuário, você pode usar AsyncTask usá-los corretamente no android development.but alguns casos precisamos personalizar os pedidos de tempo, por isso usamos Threads , mas em tópicos que não podemos usar brinde, Alertdialogs como nós usando no AsyncTask.So, precisamos de Handler separado para pop-up.

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

no exemplo acima, quero dormir meu segmento em 3seg e depois quero mostrar uma mensagem do Toast, para isso no seu manipulador de implementos 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);
       }
};

Usei switch case aqui, porque se você precisar mostrar uma mensagem diferente da mesma maneira, poderá usar switch case na classe Handler ... espero que isso ajude você

Ashana.Jackol
fonte
0

Isso geralmente acontece quando algo no thread principal é chamado de qualquer thread em segundo plano. Vamos ver um exemplo, por exemplo.

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


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

No exemplo acima, estamos definindo texto no textview que está no thread principal da interface do usuário do método doInBackground (), que opera apenas em um thread de trabalho.

Prashant Paliwal
fonte
0

Eu tive o mesmo problema e o corrigi simplesmente colocando a Toast na função de substituição onPostExecute () do Asynctask <> e funcionou.

alibabaei12
fonte
-2

eu uso o código a seguir para mostrar a mensagem do segmento não principal "contexto",

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

em seguida, use o seguinte:

class myClass implements IShowMessage{

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