Android Thread/Handler 错误IllegalStateException: 指定的消息队列同步屏障令牌尚未发布
Android Thread/Handler Error IllegalStateException: The specified message queue synchronization barrier token has not been posted
我正在尝试做什么
使用处理程序和后台线程每 3 秒 x 10 次在 UI 线程上更新 TextView。 UI 上的输出应该是 "Repeat: 1" 开始,每 3 秒后,它应该 ++ 数字。例如 "Repeat: 1" 在 3 秒后更新为 "Repeat: 2",然后在再过 3 秒后更新为 "Repeat: 3"。
我是怎么做到的
我正在测试的第一种方法是使用带有 Thread.sleep() 的 for 循环来为每个循环造成 n 秒的延迟。在每个循环中,我调用 sendMessage(message) 方法,我的理论是每次都会调用 UI 线程上的 handleMessage() 方法。这是代码:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private TextView repeat;
private Handler mHandler;
private Thread backgroundThread;
private String uiString;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate(Bundle) called by Gil ");
repeat = (TextView)findViewById(R.id.view_repeat);
runInBackground();
backgroundThread.start();
mHandler = new Handler() {
@Override
public void handleMessage(Message message) {
Bundle bundle = message.getData();
uiString = bundle.getString("count");
repeat.setText("Repeat: " + uiString);
}
};
}
public void runInBackground() {
Log.d(TAG, "runInBackGround() called by Gil");
backgroundThread = new Thread(new Runnable() {
@Override
public void run() {
Log.d(TAG, "Background Thread started by Gil");
Bundle bundle = new Bundle();
Message message = new Message();
for (int i = 1; i < 11; i++) {
bundle.putString("count", ""+i);
message.setData(bundle);
mHandler.sendMessage(message);
try {
// delay for 3 seconds
Thread.sleep(3000);
} catch (Throwable t) {
Log.d(TAG, "Throwable Error caused by Thread.Sleep() & Gil");
}
}
}
});
}
}
我运行
时的结果
它更新一次:"Repeat: 1" --> "Repeat: 2" 然后应用程序 stops/quits 自己更新。错误堆栈跟踪如下:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: personal.development.gilho.timerstuff, PID: 29226
java.lang.IllegalStateException: The specified message queue synchronization barrier token has not been posted or has already been removed.
at android.os.MessageQueue.removeSyncBarrier(MessageQueue.java:289)
at android.os.Looper.removeSyncBarrier(Looper.java:316)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1251)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6521)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:813)
at android.view.Choreographer.doCallbacks(Choreographer.java:613)
at android.view.Choreographer.doFrame(Choreographer.java:583)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:799)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:146)
at android.app.ActivityThread.main(ActivityThread.java:5679)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107)
at dalvik.system.NativeStart.main(Native Method)
Application terminated.
到目前为止我做了什么
- 这个SO Question:建议使用
dispatchMessage()
而不是sendMessage()
。试过了,还是一样的错误。
- 这个SO Question:和我的问题非常相似,但是没有答案,评论推荐尝试一本关于游戏开发的书。
- 这 SO Question: 似乎是一个不同的问题,因为这里的问题与回收邮件有关,而这里没有回收发生。
- 这SO Question:结果是相似的,但不是没有重复尝试不断更新处理程序。答案也不适用我的场景。
我怀疑在每个循环中使用 Thread.sleep()
方法的 for 循环是对我想要做的事情的愚蠢实现。我的下一个选择是尝试使用 CountDownTimer,Handler 现有的延迟函数,如 sendMessageDelayed()
或 postDelayed()
。最后,我会尝试使用 Timer
.
但就目前而言,我想在尝试其他解决方案之前帮助理解为什么这不起作用。我无法使用堆栈跟踪中提供的信息来成功识别出了什么问题,在调试模式下查看每一行也没有帮助我。
问题是 Message
对象的使用。它是一个临时对象,所以一旦它被发送到 Handler
,后台线程就不应再使用它。接收线程 "owns" 就在那一点。改变你的后台线程来做这样的事情:
for (int i = 0; i < 11; i++) {
Message msg = mHandler.obtainMessage(MY_MESSAGE_ID, i, 0);
msg.sendToTarget();
// Delay or otherwise wait
}
在您的 UI 线程的处理程序上,您可以获取消息并对其进行处理:
@Override
public void handleMessage(Message message) {
switch (message.what) {
case MY_MESSAGE_ID:
repeat.setText("Repeat: " + message.arg1);
break;
default:
Log.w(TAG, "Invalid message received: " + message.what);
break;
}
}
我正在尝试做什么
使用处理程序和后台线程每 3 秒 x 10 次在 UI 线程上更新 TextView。 UI 上的输出应该是 "Repeat: 1" 开始,每 3 秒后,它应该 ++ 数字。例如 "Repeat: 1" 在 3 秒后更新为 "Repeat: 2",然后在再过 3 秒后更新为 "Repeat: 3"。
我是怎么做到的
我正在测试的第一种方法是使用带有 Thread.sleep() 的 for 循环来为每个循环造成 n 秒的延迟。在每个循环中,我调用 sendMessage(message) 方法,我的理论是每次都会调用 UI 线程上的 handleMessage() 方法。这是代码:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private TextView repeat;
private Handler mHandler;
private Thread backgroundThread;
private String uiString;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate(Bundle) called by Gil ");
repeat = (TextView)findViewById(R.id.view_repeat);
runInBackground();
backgroundThread.start();
mHandler = new Handler() {
@Override
public void handleMessage(Message message) {
Bundle bundle = message.getData();
uiString = bundle.getString("count");
repeat.setText("Repeat: " + uiString);
}
};
}
public void runInBackground() {
Log.d(TAG, "runInBackGround() called by Gil");
backgroundThread = new Thread(new Runnable() {
@Override
public void run() {
Log.d(TAG, "Background Thread started by Gil");
Bundle bundle = new Bundle();
Message message = new Message();
for (int i = 1; i < 11; i++) {
bundle.putString("count", ""+i);
message.setData(bundle);
mHandler.sendMessage(message);
try {
// delay for 3 seconds
Thread.sleep(3000);
} catch (Throwable t) {
Log.d(TAG, "Throwable Error caused by Thread.Sleep() & Gil");
}
}
}
});
}
}
我运行
时的结果它更新一次:"Repeat: 1" --> "Repeat: 2" 然后应用程序 stops/quits 自己更新。错误堆栈跟踪如下:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: personal.development.gilho.timerstuff, PID: 29226
java.lang.IllegalStateException: The specified message queue synchronization barrier token has not been posted or has already been removed.
at android.os.MessageQueue.removeSyncBarrier(MessageQueue.java:289)
at android.os.Looper.removeSyncBarrier(Looper.java:316)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1251)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6521)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:813)
at android.view.Choreographer.doCallbacks(Choreographer.java:613)
at android.view.Choreographer.doFrame(Choreographer.java:583)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:799)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:146)
at android.app.ActivityThread.main(ActivityThread.java:5679)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107)
at dalvik.system.NativeStart.main(Native Method)
Application terminated.
到目前为止我做了什么
- 这个SO Question:建议使用
dispatchMessage()
而不是sendMessage()
。试过了,还是一样的错误。 - 这个SO Question:和我的问题非常相似,但是没有答案,评论推荐尝试一本关于游戏开发的书。
- 这 SO Question: 似乎是一个不同的问题,因为这里的问题与回收邮件有关,而这里没有回收发生。
- 这SO Question:结果是相似的,但不是没有重复尝试不断更新处理程序。答案也不适用我的场景。
我怀疑在每个循环中使用 Thread.sleep()
方法的 for 循环是对我想要做的事情的愚蠢实现。我的下一个选择是尝试使用 CountDownTimer,Handler 现有的延迟函数,如 sendMessageDelayed()
或 postDelayed()
。最后,我会尝试使用 Timer
.
但就目前而言,我想在尝试其他解决方案之前帮助理解为什么这不起作用。我无法使用堆栈跟踪中提供的信息来成功识别出了什么问题,在调试模式下查看每一行也没有帮助我。
问题是 Message
对象的使用。它是一个临时对象,所以一旦它被发送到 Handler
,后台线程就不应再使用它。接收线程 "owns" 就在那一点。改变你的后台线程来做这样的事情:
for (int i = 0; i < 11; i++) {
Message msg = mHandler.obtainMessage(MY_MESSAGE_ID, i, 0);
msg.sendToTarget();
// Delay or otherwise wait
}
在您的 UI 线程的处理程序上,您可以获取消息并对其进行处理:
@Override
public void handleMessage(Message message) {
switch (message.what) {
case MY_MESSAGE_ID:
repeat.setText("Repeat: " + message.arg1);
break;
default:
Log.w(TAG, "Invalid message received: " + message.what);
break;
}
}