问题
What I am trying to do
Update TextView on the UI Thread every 3 seconds x 10 times using a Handler and a background thread. The output on the UI should be "Repeat: 1" to begin with and after every 3 seconds, it should ++ the number. Eg "Repeat: 1" updates to "Repeat: 2" after 3 seconds and then to "Repeat: 3" after further 3 seconds.
How I am trying to do that
The first method that I am testing is to use a for loop with a Thread.sleep() to cause a n second delay for each loop. In each loop, I call the sendMessage(message) method and my theory is that the handleMessage() method on the UI Thread will be called every time. Here is the code:
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");
}
}
}
});
}
}
The result when I run
It updates once: "Repeat: 1" --> "Repeat: 2" and then the application stops/quits by itself. The error stack trace is as follows:
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.
What I have done so far
- This SO Question: recommends using
dispatchMessage()
instead ofsendMessage()
. Tried it and the same error happens. - This SO Question: very similar problem as mine but there was no answer and the comments recommended to try a book on game development.
- This SO Question: appears to be a different problem as the problem here related to recycling the message whereas there is no recycling happening here.
- This SO Question: the outcome is similar but not there is no repeated attempt to continuously update the handler. The answer also not applicable to my scenario.
I suspect that a for loop with a Thread.sleep()
method in each loop is a stupid implementation of what I am trying to do. My next choices are to try to use a CountDownTimer, Handler's existing delay functions like sendMessageDelayed()
or postDelayed()
. Finally, I will try to use a Timer
.
But for now, I would like help to understand why this isn't working to learn before I try other solutions. I havent been able to use the information provided in the stacktrace to successfully identify what is going wrong nor has looking at each line in debug mode helped me.
回答1:
The problem is the use of Message
object. It is a transient object, so once it has been sent to the Handler
, the background thread should no longer use it. The receiving thread "owns" it at that point. Change you background thread to do something like this:
for (int i = 0; i < 11; i++) {
Message msg = mHandler.obtainMessage(MY_MESSAGE_ID, i, 0);
msg.sendToTarget();
// Delay or otherwise wait
}
On your UI thread's handler, you can then get the message and process it:
@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;
}
}
来源:https://stackoverflow.com/questions/42554809/android-thread-handler-error-illegalstateexception-the-specified-message-queue