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)

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


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) .


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

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.


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

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.


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.


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:)


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

}

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

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
}

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

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.


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

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

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

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.


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

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ê


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.


0

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


-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();
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.