以下例外意味着什么;我该怎么解决?
这是代码:
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)
你是从工作线程调用它。您需要从主线程中调用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()。
这是因为Toast.makeText()是从工作线程调用的。应该像这样从主UI线程调用它
runOnUiThread(new Runnable() {
public void run() {
Toast toast = Toast.makeText(mContext, "Something", Toast.LENGTH_SHORT);
}
});
ChicoBird的答案为我工作。我所做的唯一改变是创建了我必须做的UIHandler
HandlerThread uiThread = new HandlerThread("UIHandler");
Eclipse拒绝接受任何其他内容。我想是有道理的。
此外,uiHandler
显然是一个全球定义的类。我仍然没有声称理解Android是如何做到这一点以及发生了什么,但我很高兴它的工作原理。现在我将继续研究它,看看我是否能理解Android正在做什么以及为什么必须经历所有这些环节和循环。感谢ChicoBird的帮助。
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(mContext, "Message", Toast.LENGTH_SHORT).show();
}
});
对于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();
});
}
精彩的Kotlin解决方案:
runOnUiThread {
// Add your ui thread code here
}
当我的回调尝试显示对话框时,我遇到了同样的问题。
我用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();
}
});
}
Handler handler2;
HandlerThread handlerThread=new HandlerThread("second_thread");
handlerThread.start();
handler2=new Handler(handlerThread.getLooper());
现在handler2将使用不同的Thread来处理消息而不是主线程。
要在线程中显示对话框或烤面包机,最简洁的方法是使用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()
...
Toast,AlertDialogs需要在UI线程上运行,你可以使用Asynctask在android开发中正确使用它们。但有些情况我们需要自定义超时,所以我们使用Threads,但在线程中我们不能使用Toast,Alertdialogs就像我们使用的那样在AsyncTask.So我们需要单独的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 ...希望这会对你有所帮助
当从任何后台线程调用主线程上的某些内容时,通常会发生这种情况。让我们看一个例子。
private class MyTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
textView.setText("Any Text");
return null;
}
}
在上面的示例中,我们在textInview上设置文本,该文本视图位于doInBackground()方法的主UI线程中,该方法仅在工作线程上运行。
你需要从UI线程调用Toast.makeText(...)
:
activity.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(activity, "Hello", Toast.LENGTH_SHORT).show();
}
});
这是从another (duplicate) SO answer复制粘贴。
我有同样的问题,我只是通过将Toast放在Asynctask <>的onPostExecute()覆盖函数中来修复它并且它有效。
首先调用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()
我使用以下代码来显示来自非主线程“context”的消息,
@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();
}
}
最好的选择是使用RxAndroid
(RxJava
的特异性结合)对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)
现在有各种方法可以解决这个问题。
我将通过代码示例解释它:
new Thread()
{
public void run()
{
myactivity.this.runOnUiThread(new Runnable()
{
public void run()
{
//Do your UI operations like dialog opening or Toast here
}
});
}
}.start();
用于为线程运行消息循环的类。默认情况下,线程没有与之关联的消息循环;创建一个,在运行循环的线程中调用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允许您在用户界面上执行异步工作。它在工作线程中执行阻塞操作,然后在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允许您发送和处理与线程的MessageQueue关联的Message和Runnable对象。
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;
}
});
试试这个,当你看到由于Looper没有在handler之前准备好而导致的runtimeException。
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
@Override
public void run() {
// Run your task here
}
}, 1000 );
只应从Main / UI线程调用Toast.makeText()
。 Looper.getMainLooper()帮助您实现它:
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Toast toast = Toast.makeText(mContext, "Something", Toast.LENGTH_SHORT);
}
});
此方法的一个优点是您可以在没有Activity(或没有Context)的类中使用它。
我遇到了同样的问题,这就是我修复它的方法:
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());
希望这可以帮助。
错误原因:
工作线程用于执行后台任务,除非您调用runOnUiThread之类的方法,否则无法在工作线程中的UI上显示任何内容。如果你试图在UI线程上显示任何内容而不调用runOnUiThread,那么将会有一个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
。
我收到此错误,直到我执行以下操作。
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();
}
}
);
}
}
这就是我做的。
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Toast(...);
}
});
可视组件被“锁定”到来自外部线程的更改。因此,由于toast在主屏幕上显示由主线程管理的内容,因此您需要在该线程上运行此代码。希望有帮助:)