Thought I would share some weird code with you all to see if I have done anything blindingly wrong.
I have passed a handler to a background thread to update the UI of the mainactivity (I know it is not the best way but this is supposed to be a quick and dirty test app)
Basically, the code appends a message on to the textview, like so:
uiHandler.post(new Runnable() {
#Override
public void run() {
Log.i("Text2Server", "Updating log with: " + logput);
logTextView.append(logput);
}
});
This happens everytime a UDP message is received, the log is output everytime perfectly, but the textview only updates on the first message, or sometimes not at all.
I assume it is something to do with my textview but I thought I would see if anyone has seen anything similar?
Thanks!
P.S. I have tried the basics, like using setText() instead of append() etc.
Does it output "Text2Server", "Updating log with: " + logput in the LogCat?
.post(Runnable r) this runnable is placed on the queue and will be run later when the thread that is attached executes.
Maybe, the handler does not gain access to the ui thread, this assumption can be wrong as well.
Try another way.
Instantiate the handler:
Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
String logPutFromThread=msg.getData().getString("somekey");
Log.i("Text2Server", "Updating log with: " + logPutFromThread);
logTextView.append(logPutFromThread);
}
};
and from the Runnable of the thread do
Runnable updateActivityUi = new Runnable(){
#Override
public void run() {
//do something before you send the message
Message msg = new Message();
msg.peekData().putString("somekey", logput)
handler.sendMessage(msg)
}};
Related
I have a legacy IntentService that attempts to use Toast messages to display error messages.1 I'd like the messages to be displayed, and have added code to get them on the correct thread. The simplest change would be to pass in the constructed Toast object and then display it on the UI thread. However, the Toast only displays if I make it in the posted runnable, not if I pass in a pre-made Toast.
This works:
#Override
protected void onHandleIntent(Intent intent) {
showToast("Error", Toast.LENGTH_LONG);
}
private void showToast(final String msg, final int duration) {
new Handler(getMainLooper()).post(new Runnable() {
#Override
public void run() {
// Make and show the toast in the posted runnable
Toast.makeText(getApplicationContext(), msg, duration).show();
}
});
}
This doesn't work:
#Override
protected void onHandleIntent(Intent intent) {
// Make the toast here
Toast myToast = Toast.makeText(getApplicationContext(), "Error", Toast.LENGTH_LONG);
showToast(myToast);
}
private void showToast(final Toast toast) {
new Handler(getMainLooper()).post(new Runnable() {
#Override
public void run() {
// Show the toast here
toast.show();
}
});
}
In both cases, the context is the application context, and I didn't see anything in the source that would cause one version to work, but the other not. Instead the latter has the same problems as if the Toast was shown directly in the IntentService: "Handler (android.os.Handler) {...} sending message to a Handler on a dead thread", Toast not disappearing, etc.
Why does the Toast have to be made on the main thread instead of just shown there?
1. Legacy = I don't think displaying error messages in Toasts is great UI, and I don't think services displaying messages to users directly is a good idea, but that's the code I was handed and I'd like to make it this little bit better.
In the second code that you've posted, the Toast is created in the background thread which has a looper and handler set up (that is the point of IntentService).
The toast uses the current thread's looper to create a handler, but once the IntentService is finished processing the work in onHandleIntent it stops itself (if there aren't other intents to process) - destroying the thread that your Toast's handler is relying on.
line 327: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/widget/Toast.java
Making the toast in the runnable works because at that point, the current thread is the UI thread.
I am a noob learning Android via a book, i have a quick question. My book code is pretty simple and looks like this:
My handler:
Handler handler=new Handler() {
#Override
public void handleMessage(Message msg) {
bar.incrementProgressBy(5);
}
};
My thread:
Thread background=new Thread(new Runnable() {
public void run() {
try {
for (int i=0;i<20 && isRunning.get();i++) {
Thread.sleep(1000);
handler.sendMessage(handler.obtainMessage());
}
}
catch (Throwable t) {
// just end the background thread
}
}
});
My question is here:
handler.sendMessage(handler.obtainMessage());
What the heck is "handler.obtainMessage()" ?
Doing a mouse over in Eclipse gives me a message that sounds like gibberish.
What message is it trying to "obtain"?
As described in the docs, it obtains a message from the message pool instead of creating a new one. (you need to send a message to the handler anyway):
Returns a new Message from the global message pool. More efficient
than creating and allocating new instances. The retrieved message has
its handler set to this instance (Message.target == this). If you
don't want that facility, just call Message.obtain() instead.
I'll try to elaborate:
You send a message to the handler. The message is added to the handler's thread queue and processed on the original thread. You need to send it a message, though you have nothing specific in the message that it uses (according to your handler code) so you just send an empty message, but instead of allocating a memory for a new message, the message is taken from the message pool, which is faster.
Hope this makes things clearer.
Regarding how to set a message with an int:
Message m = new Message();
Bundle b = new Bundle();
b.putInt("what", 5); // for example
m.setData(b);
handler.sendMessage(m);
I need to display a message to the user "Communicating to the Server...Please wait for few seconds" when a call to a webservice is made. Currently I'm using Toast.makeText to display the message. For some reason, I don't see the message pop-up. But interestingly when I comment the web service method call, I see the Toast message.
Toast.makeText(this, "Communicating to the Server...Please wait for few seconds",
Toast.LENGTH_SHORT).show();
//webservice code goes here...
Or any other alternative to satisfy this requirement is also fine.
Have you looked at using AysncTask. Using AsyncTask you can show a dialog with your message on onPreExecute().
Do NOT mix UI code and network code. See: http://developer.android.com/resources/articles/painless-threading.html
You can use AsyncTask to run your service and show Toast in onPreExecute.
Or you can use normal Thread but, you'll need to use Handler. Here is how:
class MyActivity extends Activity
{
final Handler mHandler;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(...);
mHandler = new Handler();
...
}
void showToast(final String text)
{
mHandler.post(new Runnable()
{
#Override
public void run()
{
Toast.makeText(MyActivity.this, text, Toast.LENGTH_LONG).show();
}
});
}
class MyThread implements Runnable
{
#Override
public void run()
{
showToast("your custom text");
//your service code
}
}
}
And here is how you start the thread:
Thread thread = new Thread(new MyThread());
thread.run();
The problem is that the UI thread is blocked as soon as you make the blocking web service call, so it never updates with the toast message. By the time it returns, the time for toast message has expired.
Run your web service call in a thread, using AsyncTask, or just create a thread like,
new Thread(new Runnable() {
public void run() {
// WS call here
}
}).start();
Take care that if you create your own thread, you can only update the UI from the UI thread, so you'll need to use Handler.post() or sendMessage() to run the UI update on the UI thread.
http://developer.android.com/reference/android/os/AsyncTask.html
http://developer.android.com/reference/android/os/Handler.html
I have a problem. I'm using a FileObserver, which moves new files from the watched directories to another, former specified directory. In my thoughts there should be shown a toast message that says 'File xy has been moved', as long as the observer watches the directory, also if the applications is only in the background. But I didn't get it working.
It always tells me, that there is a RuntimeException, and that it cannot been done without calling Looper.prepare().
05-11 13:21:28.484:
WARN/System.err(3397):
java.lang.RuntimeException: Can't
create handler inside thread that has
not called Looper.prepare()
I tried the way with using an handler too, but I also didn't get it to work.
Has someone else an idea?
Thanks in advance.
Best regards, Tobi
Before your Toast statement add the following :
runOnUiThread(new Runnable() {
public void run()
{
Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();
}
});
This will make it run on UI thread.
Hope this helps.
Obviously, your FileObserver runs(or is) another thread. You can not modify the UI from non-UI thread. Pass a Handler to your FileObserver and send messages from it. Read about Handlers.
What are you using for the context of the Toast message? That will have to have a way to display something on the screen.
Put the following code in your class:
// Need handler for callbacks to UI Threads
// For background operations
final Handler mHandler = new Handler();
// Create Runnable for posting results
final Runnable mUpdateResults = new Runnable() {
public void run() {
// Show the toast here.
}
};
and in your fileobserver's thread call place following fragment of code:
mHandler.post(mUpdateResults);
and don't use the getApplicationContext() instead try YourClassPhysicalName.java for the context of the Toast.
In my OnCreate method I have created a thread that listens to incoming message!
In OnCreate() {
//Some code
myThread = new Thread() {
#Override
public void run() {
receiveMyMessages();
}
};
myThread.start();
// Some code related to sending out by pressing button etc.
}
Then, receiveMyMessage() functions…
Public void receiveMyMessage()
{
//Receive the message and put it in String str;
str = receivedAllTheMessage();
// << here I want to be able to update this str to a textView. But, How?
}
I checked this article but it did not work for me, no luck!
Any updates to the UI in an Android application must happen in the UI thread. If you spawn a thread to do work in the background you must marshal the results back to the UI thread before you touch a View. You can use the Handler class to perform the marshaling:
public class TestActivity extends Activity {
// Handler gets created on the UI-thread
private Handler mHandler = new Handler();
// This gets executed in a non-UI thread:
public void receiveMyMessage() {
final String str = receivedAllTheMessage();
mHandler.post(new Runnable() {
#Override
public void run() {
// This gets executed on the UI thread so it can safely modify Views
mTextView.setText(str);
}
});
}
The AsyncTask class simplifies a lot of the details for you and is also something you could look into. For example, I believe it provides you with a thread pool to help mitigate some of the cost associated with spawning a new thread each time you want to do background work.
Android supports message-passing concurrency using handlers and sendMessage(msg). (It is also possible to use handlers for shared-memory concurrency.) One tip is to call thread.setDaemon(true) if you wish the thread to die when the app dies. The other tip is to have only one handler and use message.what and a switch statement in the message handler to route messages.
Code and Code