30552

Handler changing UI causes CalledFromWrongThreadException

I've created a Handler that can be accessed from anywhere within the activity and also written a method to make it easier to call 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 then this can be called from a background thread simply with:

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

This will work 1+ times, however, the actual visual change will cause a CalledFromWrongThreadException to be thrown. Being new to Java & Android, I'm stuck on why this is happening.

I have noticed that the crash tends to happen when there's a slightly longer time period between calls & that if the calls to TextOutputWrapper(String) are happening very quickly after one another, then it works. For example, this:

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

works fine.

Having looked at LogCat, it seems that the garbage collector frees up some resources and then the next time TextOutputWrapper(String) is called, it crashes (when Output.SetText(String) is called, to be precise), although I'm not exactly sure why that would cause this error.

Answer1:

There's a few things I'd change here:

Using Handler

A Handler is useful if you want to trigger the UI to update, and do so from a <strong>non-UI thread</strong> (aka "background" thread).

In your case, it's not serving that purpose. You are <strong>directly</strong> calling

textFromBGThread.handleMessage(msg);

It's not designed for you to do that. The way you are supposed to use Handler is to implement what you want done to the UI in the handleMessage(Message) method. You did that. But, you shouldn't directly call handleMessage(). If you do that, then handleMessage() will be called from whatever thread invokes TextOutputWrapper(). <strong>If that's a background thread, then that's wrong.</strong>

What you want to do is to call the handler's sendMessage(Message) method (or one of the other available variants). sendMessage() will put your message in a thread-safe queue, that is then processed on the main thread. The main thread will then invoke your handler's handleMessage(), passing it back the queued message, and allowing it to safely change the UI. So, change 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 bit hard to read, for an experienced Java developer. In Java, typical coding standards reserve upper case names for things like classes, while methods start with lower case letters. So, please rename the method to:

private void textOutputWrapper (String outputText);

or, better yet, since this is in fact a method, and not a wrapper, per se, rename to something like

private void outputText(String text);

Safe Threading Alternatives

Finally, I might recommend that if you simply want a method that allows you to safely modify the UI from any thread, use another technique. I don't find Handler to be that 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'll also point you to some general docs on understanding threading in Android:

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

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

Recommend

  • What is happening when this code calls FUSE like this?
  • const char **a = {“string1”,“string2”} and pointer arithametic
  • Exclusive access established by another Thread Java smartcardio
  • Is there a way to use Firebase Legacy console from new account
  • Process.PrivateMemorySize64 returning committed memory instead of private
  • Override bootstrap style not working
  • toInstant() in Calendar is showing in GMT instead of Local time
  • UIImageJPEGRepresentation giving 2x images on retina display
  • Visual Studio not stopping on an exception being thrown
  • Unknown C# type
  • What is the first step to using a REST API in Rails?
  • What dll is needed for Windows.Devices.Geolocation?
  • Insert space after period using sed
  • not able to create VC++ project, with VS11
  • Visual studio 2015 keystroke with mouse button
  • How does inheritance and polymorphism work in this situation?
  • CKeditor stripping font tags instead of converting to span
  • How to open multiple instances of a program in Linux
  • Haskell: function composition with anonymous/lambda function
  • How do I add a File Type Association in a Windows Phone 8.1 app manifest?
  • Changing media screen makes div overlay
  • Double dispatch in Java example
  • Python PIL to extract number from image
  • Issue with routerLink directive
  • How to pass solution folder as parameter in command line arguments (for debug)?
  • SyntaxError: (irb):26: both block arg and actual block given
  • Salesforce Different WSDL files and when to use
  • ASP.NET MVC 2 Preview 2 - display directory list rather than home/index
  • presentShareDialogWithParams posts to FB wall, but callback handler results say error
  • Time complexity of a program which involves multiple variables
  • Checking free space on FTP server
  • Why HTML5 Canvas with a larger size stretch a drawn line?
  • TFS: Get latest causes slow project reloading
  • Controls, properties, events and timers running in design time
  • WinForms: two way TextBox problem
  • Calling of Constructors in a Java
  • How do I rollback to a specific git commit
  • Traverse Array and Display in markup
  • Transpose CSV data with awk (pivot transformation)
  • Why can't I rebase on to an ancestor of source changesets if on a different branch?