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.
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 non-UI thread (aka "background" thread).
In your case, it's not serving that purpose. You are directly 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()
. If that's a background thread, then that's wrong.
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