Не удается создать обработчик внутри потока, который не вызвал 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();
}
}