Java.lang.RuntimeException: невозможно создать обработчик внутри потока, который не вызвал Looper.prepare();

У меня есть приложение для Android, в котором работает поток. Я хочу, чтобы сообщение Toast отображалось с сообщением.

Когда я это сделаю, я получаю следующее исключение:

Трассировка Logcat:

FATAL EXCEPTION: Timer-0 
 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$TN.<init>(Toast.java:322)
 at android.widget.Toast.<init>(Toast.java:91)
 at android.widget.Toast.makeText(Toast.java:238) 

Есть ли работа для того, чтобы нажимать Toast-сообщения из потоков в пользовательский интерфейс?

Ответ 1

У меня есть это исключение, потому что я пытался создать всплывающее окно Toast из фонового потока.
Toast требует действия для нажатия на пользовательский интерфейс, а потоки не имеют этого.
Поэтому одним из способов является предоставление потоку ссылки на родительскую активность и тосты.

Поместите этот код в поток, в который вы хотите отправить сообщение Toast:

parent.runOnUiThread(new Runnable() {
    public void run() {
        Toast.makeText(parent.getBaseContext(), "Hello", Toast.LENGTH_LONG).show();
    }
});

Держите ссылку на родительскую активность в фоновом потоке, который создал этот поток. Используйте родительскую переменную в вашем классе потоков:

private static YourActivity parent;

Когда вы создаете поток, передайте родительскую активность в качестве параметра через конструктор, например:

public YourBackgroundThread(YourActivity parent) {
    this.parent = parent;
}

Теперь фоновый поток может вызывать сообщения Toast на экран.

Ответ 2

Android в основном работает с двумя типами потоков, а именно с потоком пользовательского интерфейса и фоновым потоком. Согласно документации по Android -

Не обращайтесь к инструментарию Android UI из-за пределов пользовательского интерфейса, чтобы исправить эту проблему, Android предлагает несколько способов доступа к потоку пользовательского интерфейса из других потоков. Вот список методов, которые могут помочь:

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

Теперь для решения этой проблемы существуют различные методы. Я объясню это по образцу кода

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

Класс, используемый для запуска цикла сообщений для потока. Потоки по умолчанию не имеют связанного с ними цикла сообщений; для его создания вызовите prepare() в потоке, который должен запустить цикл, а затем loop(), чтобы обработать сообщения до тех пор, пока цикл не будет остановлен.

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 позволяет выполнять асинхронную работу с вашим пользовательским интерфейсом. Он выполняет операции блокировки в рабочем потоке и затем публикует результаты в потоке пользовательского интерфейса, не требуя, чтобы вы сами обрабатывали потоки и/или обработчики.

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

Обработчик

Обработчик позволяет отправлять и обрабатывать объекты Message и Runnable, связанные с потоком 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;
        }
    });

Ответ 3

Из http://developer.android.com/guide/components/processes-and-threads.html:

Кроме того, набор инструментов Android UI не является потокобезопасным. Итак, вы не должен управлять вашим пользовательским интерфейсом из рабочего потока - вы должны делать все манипуляции с пользовательским интерфейсом из потока пользовательского интерфейса. Таким образом, это просто два правила для модели с одним потоком в Android:

  • Не блокируйте поток пользовательского интерфейса
  • Не обращайтесь к инструментарию Android UI из-за пределов пользовательского интерфейса.

Вы должны обнаружить праздность в рабочем потоке и показать тост в основном потоке.

Пожалуйста, разместите код, если вы хотите получить более подробный ответ.

После публикации кода:

В strings.xml

<string name="idleness_toast">"You are getting late do it fast"</string>

В YourWorkerThread.java

Toast.makeText(getApplicationContext(), getString(R.string.idleness_toast), 
    Toast.LENGTH_LONG).show();

Не используйте AlertDialog, сделайте выбор. AlertDialog и Toast - это две разные вещи.

Ответ 4

Вот что я делал:

  public void displayError(final String errorText) {
    Runnable doDisplayError = new Runnable() {
        public void run() {
            Toast.makeText(getApplicationContext(), errorText, Toast.LENGTH_LONG).show();
        }
    };
    messageHandler.post(doDisplayError);
}

Это должно позволить вызывать метод из любого потока.

Где messageHandler объявляется в активности как..

Handler messageHandler = new Handler();

Ответ 5

Вы можете просто использовать BeginInvokeOnMainThread(). Он вызывает действие в потоке основного устройства (UI).

Device.BeginInvokeOnMainThread(() => { displayToast("text to display"); });

Это просто и отлично работает для меня!

EDIT: Работает, если вы используете С# Xamarin