Changing the handler user interface raises a CalledFromWrongThreadException

I created a Handler that can be accessed from anywhere within the scope of the action, and also wrote a method to simplify the invocation of the handler:

 private Handler textFromBGThread = new Handler() { @Override public void handleMessage (Message msg) { // Get the string from the msg String outputString = msg.getData().getString("Output"); // Find the TextView TextView Output = (TextView)findViewById(R.id.ConsoleOutputView); // Display the output Log.i("TextOutput","About to display message: " + outputString); Output.setText(Output.getText() + outputString); Log.i("TextOutput","Message displayed"); } }; private void TextOutputWrapper (String outputText) { Message msg = new Message(); Bundle bndle = new Bundle(); bndle.putString("Output", "\n" + outputText); msg.setData(bndle); textFromBGThread.handleMessage(msg); } 

So, this can be called from the background thread simply by using:

 TextOutputWrapper("Attemping to connect..."); 

This will work 1 more time, however the actual visual change will raise a CalledFromWrongThreadException . Being new to Java and Android, I get stuck on why this is happening.

I noticed that a crash tends to occur when there is a slightly longer period of time between calls and that if calls to TextOutputWrapper(String) occur very quickly one after another, then this works. For example, this:

 int i = 0; while (i < 200) { TextOutputWrapper(String.valueOf(i)); i++; } 

works great.

Looking at LogCat, it seems that the garbage collector frees up some resources, and then the next time TextOutputWrapper(String) is called, it fails (when Output.SetText(String) is called, to be precise), although I’m not quite sure why this will result to this error.

+2
source share
1 answer

Here are a few things I would change here:

Using a Handler

A Handler is useful if you want to activate the user interface for updating and do it from a non-UI stream (also called "background").

In your case, this does not serve this purpose. You directly call

 textFromBGThread.handleMessage(msg); 

It is not meant for you. The way you use Handler is to implement what you want to do with the user interface in the handleMessage(Message) method. You did it. But you should not directly call handleMessage() . If you do this, then handleMessage() will be called from any thread that calls TextOutputWrapper() . If this is a background thread, then this is wrong.

What you want to do is call the sendMessage (Message) method (or one of the other options available). sendMessage() places your message in a thread-safe queue, which is then processed in the main thread. Then the main thread will call your handleMessage() handler, passing it a message in the queue and letting it safely change the user interface. So, modify TextOutputWrapper() to use this:

 private void TextOutputWrapper (String outputText) { Message msg = new Message(); Bundle bndle = new Bundle(); bndle.putString("Output", "\n" + outputText); msg.setData(bndle); textFromBGThread.sendMessage(msg); } 

Java conventions

This code is a little hard to read for an experienced Java developer. In Java, typical coding standards reserve uppercase names for things like classes, while methods begin with lowercase letters. So rename the method:

 private void textOutputWrapper (String outputText); 

or, even better, since it’s actually a method, and not the shell itself, is renamed to something like

 private void outputText(String text); 

Safe Threading Alternatives

Finally, I could recommend that if you just need a method that allows you to safely modify the user interface from any thread, use another method. I do not find Handler so easy to use for beginners.

 private void outputText(final String outputString) { runOnUiThread(new Runnable() { @Override public void run() { // Find the TextView TextView output = (TextView)findViewById(R.id.ConsoleOutputView); // Display the output Log.i("TextOutput","About to display message: " + outputString); output.setText(Output.getText() + outputString); Log.i("TextOutput","Message displayed"); } }); } 

runOnUiThread() is a method available in every Activity .

I will also point you to some general documents on understanding streams in Android:

http://www.vogella.com/articles/AndroidBackgroundProcessing/article.html

http://android-developers.blogspot.com/2009/05/painless-threading.html

0
source

All Articles