Не удается создать обработчик внутри потока, который не вызвал Looper.prepare()

Что означает следующее исключение: как я могу это исправить?

Это код:

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

Это исключение:

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)

Ответы

Ответ 1

Вы вызываете его из рабочего потока. Вы должны вызвать Toast.makeText() (и большинство других функций, связанных с пользовательским интерфейсом) из основного потока. Например, вы можете использовать обработчик.

Посмотрите Общение с потоком пользовательского интерфейса в документации. В двух словах:

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

Другие параметры:

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

Вы также можете использовать Activity.runOnUiThread().

Ответ 2

Вам нужно вызвать Toast.makeText(...) из потока пользовательского интерфейса:

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

Это скопировано из другого (дублирующего) SO-ответа.

Ответ 3

ОБНОВЛЕНИЕ - 2016

Лучшей альтернативой является использование RxAndroid (конкретные привязки для RxJava) для P в MVP для сбора данных за плату.

Начните с возврата Observable из существующего метода.

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

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

Используйте это наблюдение как это -

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

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

Я знаю, что немного опаздываю, но здесь. 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;
    }
});

Ответ 4

Попробуйте это, когда увидите runtimeException из-за того, что Looper не подготовлен перед обработчиком.

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

handler.postDelayed(new Runnable() {
  @Override
  public void run() {
  // Run your task here
  }
}, 1000 );

Ответ 5

Toast.makeText() должен вызываться только из потока Main/UI. Looper.getMainLooper() поможет вам достичь этого:

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

Преимущество этого метода в том, что вы можете использовать его в классах без Activity (или без Context).

Ответ 6

Я столкнулся с той же проблемой, и вот как я ее исправил:

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

Чтобы создать UIHandler, вам необходимо выполнить следующее:

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

Надеюсь, что это поможет.

Ответ 7

Причина ошибки:

Рабочие потоки предназначены для выполнения фоновых задач, и вы не можете ничего показать в пользовательском интерфейсе в рабочем потоке, если вы не вызываете метод типа runOnUiThread. Если вы попытаетесь показать что-либо в потоке пользовательского интерфейса без вызова runOnUiThread, будет java.lang.RuntimeException.

Итак, если вы находитесь в activity, но вызываете Toast.makeText() из рабочего потока, сделайте следующее:

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

Приведенный выше код гарантирует, что вы показываете сообщение Toast в UI thread, так как вы вызываете его внутри метода runOnUiThread. Поэтому не более java.lang.RuntimeException.

Ответ 8

Я получал эту ошибку, пока не сделал следующее.

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

И сделал это в одноэлементный класс:

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

}

Ответ 9

что я сделал.

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

Визуальные компоненты "заблокированы" для изменений из внешних потоков. Итак, поскольку тост показывает материал на главном экране, который управляется основным потоком, вам нужно запустить этот код в этом потоке. Надеюсь, что помогает:)

Ответ 10

Это потому, что Toast.makeText() вызывает из рабочего потока. Это должен быть вызов из основного потока пользовательского интерфейса, подобного этому

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

Ответ 11

Ответ ChicoBird работал у меня. Единственное изменение, которое я сделал, было в создании UIHandler, где мне приходилось делать

HandlerThread uiThread = new HandlerThread("UIHandler");

Eclipse отказался принимать что-либо еще. Полагаю, я полагаю.

Также uiHandler - это, очевидно, класс, определенный где-то. Я все еще не утверждаю, что понимаю, как Android это делает и что происходит, но я рад, что он работает. Теперь я приступлю к его изучению и посмотрю, смогу ли я понять, что делает Android, и почему нужно проходить все эти обручи и циклы. Спасибо за помощь ChicoBird.

Ответ 12

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

Ответ 13

Я столкнулся с той же проблемой, когда мои обратные вызовы попытались показать диалог.

Я решил его с помощью выделенных методов в Activity - на уровне экземпляра экземпляра Activity, которые используют 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();
        }
    });
}

Ответ 14

Для пользователей Rxjava и RxAndroid:

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

Ответ 15

Замечательное решение Kotlin:

runOnUiThread {
    // Add your ui thread code here
}

Ответ 16

Чтобы отобразить диалог или тостер в потоке, наиболее кратким способом является использование объекта Activity.

Например:

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

Ответ 17

Handler handler2;  
HandlerThread handlerThread=new HandlerThread("second_thread");
handlerThread.start();
handler2=new Handler(handlerThread.getLooper());

Теперь handler2 будет использовать другой поток для обработки сообщений, отличный от основного потока.

Ответ 18

Toast, AlertDialogs необходимо запустить в потоке пользовательского интерфейса, вы можете использовать Asynctask, чтобы правильно использовать их в разработке Android. Но в некоторых случаях нам нужно настроить тайм-ауты, поэтому мы используем Threads, но в потоках мы не можем использовать Toast, Alertdialogs, как мы используем в AsyncTask. Поэтому для всплывающих окон нам нужен отдельный Handler.

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

В приведенном выше примере я хочу спать с моим потоком в течение 3 секунд и после того, как хочу показать сообщение Toast, для этого в вашем обработчике реализации 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);
       }
};

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

Ответ 19

Это обычно происходит, когда что-то в основном потоке вызывается из какого-либо фонового потока. Давайте рассмотрим пример, например.

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


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

В приведенном выше примере мы устанавливаем текст в textview, который находится в основном потоке пользовательского интерфейса, из метода doInBackground(), который работает только в рабочем потоке.

Ответ 20

У меня была та же проблема, и я исправил ее, просто поместив Toast в функцию переопределения onPostExecute() в Asynctask <>, и это сработало.

Ответ 21

я использую следующий код, чтобы показать сообщение из не основного потока "контекста",

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

затем используйте как следующее:

class myClass implements IShowMessage{

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