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.
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