I've tried many ways to use handlers to receive messages on a background thread, I have not been successful.
Is there a sure fire way to test this? Is there a sample code I can use to see how it is done?
Yes, try the answer by #FoamyGuy. In the sample code he has sent back an empty message. I'm extending his code to pass strings. If you want to send some message back to the handler(eg: string), you can send some string or something else as follows:
Handler h = new Handler(){
#Override
public void handleMessage(Message msg){
if(msg.what == 1){
//Success
String msg = (String)msg.obj;
Log.d("", "Msg is:"+msg);
}else{
//Failure
String msg = (String)msg.obj;
Log.d("", "Msg is:"+msg);
}
}
};
Thread t = new Thread() {
#Override
public void run(){
doSomeWork();
if(succeed){
//we can't update the UI from here so we'll signal our handler. and it will do it for us.
Message m = h.obtainMessage(1, "Success message string");
m.sendToTarget();
}else{
Message m = h.obtainMessage(0, "Your Failed!");
m.sendToTarget();
}
}
}
On a non-UI thread? All you need to do is create a Looper on that thread, then create the handler on it. That will automatically cause that Handler to be associated with that Looper. Then run Looper.loop
So
Looper.prepare();
Handler myHandler = new Handler();
Looper.loop()
and myHandler will be on the thread.
Related
Can anyone point me in the right direction here please ?
I have an activity which spawns two threads, a thread for handling messages, using a Looper
public static class MiddleThread extends Handler{
static public Handler handler;
public void run() {
Looper.prepare();
Log.d("MiddleThread", "Looper is prepared !");
handler = new Handler() {
public void handleMessage(Message msg)
{
Bundle bundle = msg.getData();
String exitString = bundle.getString("endmessage");
if(exitString.equals(("ExitOK")))
{
boolean searchFinished = true;
Looper looper = Looper.myLooper();
looper.quit();
} else
{
int fileCount = bundle.getInt("filecount");
String fileName = bundle.getString("filename");
Log.d("MiddleThread", "File Number " + fileCount + " is " + fileName);
}
}
};
Log.d("MiddleThread", "nandler should be initialised");
Looper.loop();
}
... then it spawns the main Worker Thread, which is passed a handler from the UI Thread, and the handler from the above thread.
public class BasicSearch {
public Handler handlerUi, handlerMiddleThread;
public Message messageUi, messageMiddleThread;
public int fileCount = 0;
public BasicSearch(Handler ui, Handler mt) {
handlerUi = ui;
handlerMiddleThread = mt;
}
public void listFiles()
{
File searchPath = Environment.getExternalStorageDirectory();
messageUi = handlerUi.obtainMessage();
messageMiddleThread = handlerMiddleThread.obtainMessage();
walk(searchPath);
Bundle b = new Bundle();
b.putString("endmessage", "ExitOK");
messageMiddleThread.setData(b);
handlerMiddleThread.dispatchMessage(messageMiddleThread);
}
private void walk(File path) {
File[] list = path.listFiles();
for(File f : list)
{
if(f.isDirectory())
{
walk(new File(f.getAbsolutePath()));
} else {
processFile(f);
}
}
}
private void processFile(File f) {
Bundle b = new Bundle();
fileCount++;
b.putString("filename", f.getName());
b.putInt("filecount", fileCount);
messageMiddleThread.setData(b);
Log.d("BasicSearch", "Data is set, to send to MiddleThread");
handlerMiddleThread.dispatchMessage(messageMiddleThread);
Log.d("BasicSearch", "Message sent");
}
}
Whatever happens, when it tries to dispatchMessage, handlerMiddleThread reverts to being null. I even have the following code in my activity, to try and ensure that it isn't null, but it still ends up being null when I get to send the message.
startMiddleThread();
while(true)
{
if(MiddleThread.handler != null)
break;
}
startSearchThread();
This is a test project, as I wanted to be able to get the Handler/Looper concept properly understood before continuing on with my project.
I have successfully managed to use a Handler in my UI Threads before, but my current project has too much processing going on in the UI, and I want to have a secondary thread handling the output from the searchThread, and just receive a message in UI thread when the thread is complete.
So I think I see what you're trying to do and let me suggest a slightly easier way:
To start your background thread and get a handler to it:
HandlerThread bgThread = new HandlerThread();
bgThread.start();
Handler bgHandler = new Handler(bgThread.getLooper());
Then you can send whatever messages you want to your bgHandler. Note that you need to call start on a HandlerThread before creating the bgThread (otherwise getLooper() will return null).
That being said I think I know whats wrong with your code as you posted it. First, MiddleThread extends Handler (which doesn't have a run() method!) not Thread. Second, the run() method on MiddleThread is never called, so Handler is never instantiated. Even if your just mistyped Handler in your code above and you're actually extending Thread, you still need to call start on MiddleThread in order for anything in run() to be executed. Really though, what you're doing is waaay more complicated that it needs to be, and you almost certainly want to just do what I mentioned above.
I have code which calls a new Thread that connects to an IRC server. The thread has loop to listen for response from the IRC server and calls a method 'ProcessData' to action the response.
On my UI I want to be able to 'QUIT' the IRC server in onStop and onPause. The trouble I have is that when I use a Handler to post a message to my IRC thread which sends a QUIT command to the IRC server it tells me that I am performing network operations on the UI thread.
The handler is setup in my IRCManager class (this class extends Thread and is the class I run on a separate thread.
public Handler networkHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
try {
processData((String) msg.obj);
} catch (IOException e) {
Log.e(TAG, "network handler given an object NOT of type String");
}
super.handleMessage(msg);
}
};
I use the handler from the main activity and instantiate it just after starting the network thread
irc.start();
networkHandler = irc.networkHandler;
In the onPause event I send a message to the handler
Message msg = new Message();
msg.obj = IRCManager.QUIT;
networkHandler.sendMessage(msg);
EDIT: Here is the processData method
void processData(String data) throws IOException {
if (data.contains("PING")) {
String pingId = data.substring(6, data.length());
sendMessage(pong + pingId + "\n");
isConnected = true;
Message msg = new Message();
msg.what = 1;
msg.obj = "test";
handler.sendMessage(msg);
} else if (data.contains("Welcome")) {
sendMessage("PRIVMSG " + BOT_NAME + " JOIN " + siteId + "\n");
} else if (data.contains(IRCManager.QUIT)) {
disconnect();
} else if (isClientConnected()) {
Message msg = new Message();
msg.what = 2;
msg.obj = "test";
handler.sendMessage(msg);
}
}
It seems that the handler isn't linking properly to the thread. Can anyone shed any light on how I can do this?
My thread actually spends 99% of it's time in a while loop checking the inputstream from the IRC server. This may also have something to do with it.
You're creating the instance of the Handler, networkHandler, here:
public Handler networkHandler = new Handler() {
It'll be therefore associated with the UI thread.
And, when you say:
I use the handler from the main activity and instantiate it just after starting the network thread
irc.start();
networkHandler = irc.networkHandler;
You're not creating the instance of the Handler there; you're just grabbing a reference to it.
You actually need to create the instance of the Handler in the run() method of your non-UI Thread.
Try to use another constructor with new Handler(new Handler.Callback() ) inside
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)
}};
I am getting an Runtime Exception:Can't create handler inside thread that has not called Looper.prepare() while displaying the Toast message in a worker thread.
I have a service (runs in a remote process) which creates an object. This object is responsible for connecting to a server in a thread. I get the response from the sever. I want to display the message from the server in the toast. At that time I getting this exception. I tried posting it in a Handler by using handler.post. But still i am getting the exception.
What should be the approach to avoid this.
Define a Handler like this:
private final Handler handler = new Handler() {
public void handleMessage(Message msg) {
if(msg.arg1 == 1)
Toast.makeText(getApplicationContext(),"Your message", Toast.LENGTH_LONG).show();
}
}
Then put the following code where you need to show your toast message.
Message msg = handler.obtainMessage();
msg.arg1 = 1;
handler.sendMessage(msg);
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);