我有一个运行线程的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消息从线程推送到用户界面?
答案 0 :(得分:117)
我遇到了这个例外,因为我试图从后台线程制作一个Toast弹出窗口 Toast需要一个Activity来推送到用户界面,而线程没有那个 因此,一种解决方法是为线程提供指向父Activity和Toast的链接。
将此代码放在要发送Toast消息的线程中:
parent.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(parent.getBaseContext(), "Hello", Toast.LENGTH_LONG).show();
}
});
在创建此线程的后台线程中保留指向父Activity的链接。在线程类中使用父变量:
private static YourActivity parent;
创建线程时,将父Activity作为参数传递给构造函数,如下所示:
public YourBackgroundThread(YourActivity parent) {
this.parent = parent;
}
现在后台线程可以将Toast消息推送到屏幕。
答案 1 :(得分:33)
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允许您发送和处理与线程的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;
}
});
答案 2 :(得分:8)
以下是我一直在做的事情:
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();
答案 3 :(得分:6)
来自http://developer.android.com/guide/components/processes-and-threads.html:
此外,Android UI工具包不是线程安全的。 所以,你 不得从工作线程操纵你的UI - 你必须做所有 从UI线程操作到您的用户界面。因此,那里 只是Android单线程模型的两个规则:
- 不要阻止UI线程
- 不要从UI线程外部访问Android UI工具包
醇>
您必须检测工作线程中的空闲状态并在主线程中显示Toast。
如果您想要更详细的答案,请发布一些代码。
代码发布后:
在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 :(得分:1)
您可以简单地使用BeginInvokeOnMainThread()。它在设备主(UI)线程上调用Action。
Device.BeginInvokeOnMainThread(() => { displayToast("text to display"); });
这很简单,对我来说很完美!
编辑:如果您正在使用C#Xamarin
,则可以使用答案 5 :(得分:1)
runOnUiThread(new Runnable(){
public void run() {
Toast.makeText(getApplicationContext(), "Status = " + message.getBody() , Toast.LENGTH_LONG).show();
}
});
这对我有用
答案 6 :(得分:0)
我在JobService
中从以下代码中收到此错误:
BluetoothLeScanner bluetoothLeScanner = getBluetoothLeScanner();
if (BluetoothAdapter.STATE_ON == getBluetoothAdapter().getState() && null != bluetoothLeScanner) {
// ...
} else {
Logger.debug(TAG, "BluetoothAdapter isn't on so will attempting to turn on and will retry starting scanning in a few seconds");
getBluetoothAdapter().enable();
(new Handler()).postDelayed(new Runnable() {
@Override
public void run() {
startScanningBluetooth();
}
}, 5000);
}
服务崩溃:
2019-11-21 11:49:45.550 729-763/? D/BluetoothManagerService: MESSAGE_ENABLE(0): mBluetooth = null
--------- beginning of crash
2019-11-21 11:49:45.556 8629-8856/com.locuslabs.android.sdk E/AndroidRuntime: FATAL EXCEPTION: Timer-1
Process: com.locuslabs.android.sdk, PID: 8629
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:203)
at android.os.Handler.<init>(Handler.java:117)
at com.locuslabs.sdk.ibeacon.BeaconScannerJobService.startScanningBluetoothAndBroadcastAnyBeaconsFoundAndUpdatePersistentNotification(BeaconScannerJobService.java:120)
at com.locuslabs.sdk.ibeacon.BeaconScannerJobService.access$500(BeaconScannerJobService.java:36)
at com.locuslabs.sdk.ibeacon.BeaconScannerJobService$2$1.run(BeaconScannerJobService.java:96)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
因此,我从Handler
更改为Timer
,如下所示:
(new Timer()).schedule(new TimerTask() {
@Override
public void run() {
startScanningBluetooth();
}
}, 5000);
现在,代码不再抛出RuntimeException
。