无法在未调用Looper.prepare()的线程内创建处理程序

时间:2010-10-06 17:18:27

标签: android ui-thread android-toast

以下例外意味着什么;我该如何解决?

这是代码:

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)

30 个答案:

答案 0 :(得分:793)

您需要从UI线程中调用Toast.makeText(...)

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

这是从another (duplicate) SO answer复制粘贴的。

答案 1 :(得分:623)

你是从工作线程调用的。您需要从主线程中调用Toast.makeText()(以及处理UI的大多数其他函数)。例如,您可以使用处理程序。

在文档中查找Communicating with the UI Thread。简而言之:

// 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 :(得分:427)

更新 - 2016

最好的替代方法是使用RxAndroidRxJava的特定绑定)来P MVP来处理数据。

首先从现有方法返回Observable

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

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

像这样使用这个Observable -

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基本上适用于两种线程类型,即 UI线程后台线程。根据android文档 -

  

不要从UI线程外部访问Android UI工具包来解决此问题,Android提供了几种从其他线程访问UI线程的方法。以下列出了可以提供帮助的方法:

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()在运行循环的线程中,然后循环()到   让它处理消息,直到循环停止。

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允许您对用户执行异步工作   接口。它在工作线程中执行阻塞操作   然后在UI线程上发布结果,无需您   自己处理线程和/或处理程序。

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允许您发送和处理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 :(得分:78)

试试这个,当你看到由于Looper没有在handler之前准备好而导致的runtimeException。

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

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

答案 4 :(得分:69)

应该从Main / UI线程调用

sudo rpi-update f74b92120e0d469fc5c2dc85b2b5718d877e1cbb sudo reboot Looper.getMainLooper()帮助您实现目标:

pi@raspberrypi:~ $ sudo gatttool -b EC:EA:FA:D8:F9:77 -t random -I
[EC:EA:FA:D8:F9:77][LE]> connect
Attempting to connect to EC:EA:FA:D8:F9:77
Connection successful
[EC:EA:FA:D8:F9:77][LE]> primary
attr handle: 0x0001, end grp handle: 0x0007 uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle: 0x0008, end grp handle: 0x000b uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle: 0x000c, end grp handle: 0x0011 uuid: 713d0000-503e-4c75-ba94-3148f18d941e
attr handle: 0x0012, end grp handle: 0xffff uuid: 0000180a-0000-1000-8000-00805f9b34fb

此方法的一个优点是您可以在没有Activity(或没有Context)的类中使用它。

答案 5 :(得分:39)

我遇到了同样的问题,这就是我修复它的方法:

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

希望这有帮助。

答案 6 :(得分:32)

错误原因:

工作线程用于执行后台任务,除非您调用 runOnUiThread 之类的方法,否则无法在工作线程中的UI上显示任何内容。如果您尝试在不调用runOnUiThread的情况下在UI线程上显示任何内容,则会有java.lang.RuntimeException

因此,如果您在activity但是从工作线程调用Toast.makeText(),请执行以下操作:

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

上面的代码确保您在UI thread中显示Toast消息,因为您在runOnUiThread方法中调用它。所以不再java.lang.RuntimeException

答案 7 :(得分:21)

我收到此错误,直到我执行了以下操作。

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

}

答案 8 :(得分:19)

这就是我所做的。

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

视觉组件被锁定&#34;来自外部线程的更改。 因此,由于toast在主屏幕上显示由主线程管理的内容,因此您需要在该线程上运行此代码。 希望有所帮助:)

答案 9 :(得分:8)

这是因为Toast.makeText()是从工作线程调用的。它应该从主UI线程调用,如此

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

答案 10 :(得分:7)

ChicoBird的答案为我工作。我做的唯一改变是创建了我必须做的UIHandler

HandlerThread uiThread = new HandlerThread("UIHandler");

Eclipse拒绝接受任何其他内容。我想是有道理的。

uiHandler显然是某个全局定义的类。我仍然没有声称理解Android是如何做到这一点以及发生了什么,但我很高兴它有效。现在我将继续研究它,看看我是否能理解Android正在做什么以及为什么必须经历所有这些环和循环。感谢ChicoBird的帮助。

答案 11 :(得分:6)

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

答案 12 :(得分:4)

当我的回调尝试显示对话框时,我遇到了同样的问题。

我使用活动中的专用方法解决了它 - 在活动实例成员级别 - 使用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();
        }
    });
}

答案 13 :(得分:4)

出色的Kotlin解决方案:

runOnUiThread {
    // Add your ui thread code here
}

答案 14 :(得分:4)

对于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 :(得分:3)

首先呼叫Looper.prepare(),然后再呼叫Toast.makeText().show(),最后呼叫Looper.loop(),例如:

Looper.prepare() // to be able to make toast
Toast.makeText(context, "not connected", Toast.LENGTH_LONG).show()
Looper.loop()

答案 16 :(得分:1)

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

现在handler2将使用与主线程不同的线程来处理消息。

答案 17 :(得分:1)

要在线程中显示对话框或烤面包机,最简洁的方法是使用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()
...

答案 18 :(得分:1)

使用lambda:

activity.runOnUiThread(() -> Toast.makeText(activity, "Hello", Toast.LENGTH_SHORT).show());

答案 19 :(得分:0)

我遇到了同样的问题,这就是现在对我来说很好用的 例如,这是我在后台和 ui 线程中执行任务的代码 看看looper是如何使用的

new Thread(new Runnable() {
              @Override
              public void run() {
              Looper.prepare();
                                
                                // your Background Task here

                runOnUiThread(new Runnable() {
                  @Override
                  public void run() {

                                // update your UI here      
                                
                  Looper.loop();
                                  }
                });
              }
            }).start();

我希望这对你或其他人有帮助

答案 20 :(得分:0)

在线程外创建处理程序

final Handler handler = new Handler();

        new Thread(new Runnable() {
            @Override
            public void run() {
            try{
                 handler.post(new Runnable() {
                        @Override
                        public void run() {
                            showAlertDialog(p.getProviderName(), Token, p.getProviderId(), Amount);
                        }
                    });

                }
            }
            catch (Exception e){
                Log.d("ProvidersNullExp", e.getMessage());
            }
        }
    }).start();

答案 21 :(得分:0)

最近,我遇到了这个问题 - 之所以发生这种情况,是因为我试图调用一个函数,该函数将在构造函数中执行一些 UI 操作。从构造函数中删除初始化为我解决了这个问题。

答案 22 :(得分:0)

协程会完美地完成

CoroutineScope(Job() + Dispatchers.Main).launch {
                        Toast.makeText(context, "yourmessage",Toast.LENGTH_LONG).show()}

答案 23 :(得分:0)

Java 8

new Handler(Looper.getMainLooper()).post(() -> {
    // Work in the UI thread

}; 

科特林

Handler(Looper.getMainLooper()).post{
    // Work in the UI thread
}

GL

答案 24 :(得分:0)

以下是使用协程针对Kotlin的解决方案:

通过MainScope()使用CoroutineScope扩展您的课程:

class BootstrapActivity :  CoroutineScope by MainScope() {}

然后只需执行以下操作:

launch {
        // whatever you want to do in the main thread
    }

别忘了添加协程的依赖项:

org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.kotlinCoroutines}
org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.kotlinCoroutines}

答案 25 :(得分:0)

您需要在UI线程上创建烤面包。在下面找到示例。

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

有关显示Toast消息的信息,请参阅此article

答案 26 :(得分:0)

我遇到了同样的问题,我只是通过将Toast放在Asynctask <>的onPostExecute()覆盖函数中来解决了它,并且它起作用了。

答案 27 :(得分:0)

当从任何后台线程调用主线程上的某些内容时,通常会发生这种情况。让我们看一个例子,例如。

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


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

在上面的示例中,我们在textInview上设置文本,该文本视图位于doInBackground()方法的主UI线程中,该方法仅在工作线程上运行。

答案 28 :(得分:0)

Toast,AlertDialogs 需要在UI线程上运行,你可以使用 Asynctask 在android开发中正确使用它们。但有些情况我们需要自定义超时,所以我们使用线程,但是在线程中我们不能像在AsyncTask中那样使用Toast,Alertdialog。所以我们需要单独的 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秒内睡眠我的线程,之后我想在 mainthread 实现处理程序中显示Toast消息。

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 case,因为如果你需要以相同的方式显示不同的消息,你可以在Handler类中使用switch case ...希望这会对你有所帮助

答案 29 :(得分:-2)

我使用以下代码显示来自非主线程“context”的消息,

npm i coffee-script

然后使用如下:

npm i coffee-script -g
相关问题