How to Create a Looper Thread, Then Send It a Message Immediately

How to create a Looper thread, then send it a message immediately?

Eventual solution (minus error checking), thanks to CommonsWare:

class Worker extends HandlerThread {

// ...

public synchronized void waitUntilReady() {
d_handler = new Handler(getLooper(), d_messageHandler);
}

}

And from the main thread:

Worker worker = new Worker();
worker.start();
worker.waitUntilReady(); // <- ADDED
worker.handler.sendMessage(...);

This works thanks to the semantics of HandlerThread.getLooper() which blocks until the looper has been initialized.


Incidentally, this is similar to my solution #1 above, since the HandlerThread is implemented roughly as follows (gotta love open source):

public void run() {
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Looper.loop();
}

public Looper getLooper() {
synchronized (this) {
while (mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}

The key difference is that it doesn't check whether the worker thread is running, but that it has actually created a looper; and the way to do so is to store the looper in a private field. Nice!

How to force main thread to wait until Looper will end its work

I have tried with this code and everything seems to be working fine.

private class BackgroundThread extends HandlerThread {

private Handler handler;

public BackgroundThread(String name) {
super(name);
}

@Override
public void run() {
Looper.prepare();
backgroundHandler = new Handler() {

@Override
public void handleMessage(Message msg) {
log.finest("DEAService message:" + msg.what);
switch (msg.what) {
//message handling
}
}
};
MyObject myObject = new MyObject(arg1, arg2);
myObject.init();
myObject.attr = myAttr;
Looper.loop();
};

public synchronized void waitUntilReady() {
handler = new Handler(getMainLooper());
}
}

And start this HandlerThread with this code:

protected void startBackgroundThread() {
BackgroundThread backgroundThread = new BackgroundThread("backgroundThread");
backgroundThread.start();
backgroundThread.waitUntilReady();
}

Calling Looper more than once causes sending message to a Handler on a dead thread

If you check the source in android/os/MessageQueue.java, you can see something like the following

  if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
}

So the message queue is basically unusable after Looper.quit() has been called the first time, as it enqueues a Message with a null target, which is the magical identifier for the message queue to stop enqueuing and appear "dead".

Handlers, MessageQueue, Looper, do they all run on the UI thread?

Short answer: they all run on the same thread. If instantiated from an Activity lifecycle callback, they all run on the main UI thread.

Long answer:

A thread may have a Looper, which contains a MessageQueue. In order to use this facility, you would have to create a Looper on the current thread by calling (the static) Looper.prepare(), and then start the loop by calling (the also static) Looper.loop(). These are static because there is only supposed to be one Looper per thread.

The call to loop() usually does not return for some time, but keeps taking messages ("tasks", "commands" or whatever you like to call them) out of the MessageQueue and handles them individually (e.g. by calling back a Runnable contained in the message). When there are no messages left in the queue, the thread blocks until there are new messages. To stop a Looper, you have to call quit() on it (which probably does not stop the loop immediately, but rather sets a private flag that is checked periodically from the loop, signaling the it to stop).

However, you cannot add messages to the queue directly. Instead, you register a MessageQueue.IdleHandler to wait for a queueIdle() callback, in which you can decide if you wish to to something or not. All handlers are called in turn. (So the "queue" isn't really a queue, but instead a collection of callbacks to be called regularly.)

Note regarding the previous paragraph: This I actually guessed. I couldn't find any documentation on that, but it would make sense.

update: see ahcox' comment and his answer.

Because this is a lot of work, the framework provides the Handler class to simplify things. When you create a Handler instance, it is (by default) bound to the Looper already attached to the current thread. (The Handler knows what Looper to attach to because we called prepare() earlier, which probably stored a reference to the Looper in a ThreadLocal.)

With a Handler, you can just call post() to "put a message into the thread's message queue" (so to speak). The Handler will take care of all the IdleHandler callback stuff and make sure your posted Runnable is executed. (It might also check if the time is right already, if you posted with a delay.)

Just to be clear: the only way to actually make a looping thread do something is to post a message to it's loop. This is valid until you call quit() on the looper.


Regarding the android UI thread: At some point (probably before any activities and the like are created) the framework has set up a Looper (containing a MessageQueue) and started it. From this point on, everything that happens on the UI thread is through that loop. This includes activity lifecycle management and so on. All callbacks you override (onCreate(), onDestroy()...) are at least indirecty dispatched from that loop. You can see that for example in the stack trace of an exception. (You can try it, just write int a = 1 / 0; somewhere in onCreate()...)


I hope this makes sense. Sorry for being unclear previously.

Trying to create a thread to send a message to a socket. Getting 2 errors having to do with thread constructor

Windows has a SendMessage macro that transparently switches between the SendMessageA and SendMessageW functions depending on whether unicode is enabled or not. This macro substitution is stomping your SendMessage function.

You can add

#undef SendMessage

anywhere after

#include <WS2tcpip.h>

but this may cause problems later. I think you're best off changing the name of your SendMessage function to something that doesn't collide.

TL;DR Version

Why is this a problem? Ambiguity. Let's hack this down to an MCVE.

#include <WS2tcpip.h>
#include <string>
#include <thread>
#include <iostream>

using namespace std;

void SendMessage(string /*message*/)
{
}

int main()
{
string message = "Test";

thread sendtoSocket (SendMessage, message);

sendtoSocket.join();
}

After the Preprocessor, the program looks like

void SendMessageW(string /*message*/)
{
}

int main()
{
string message = "Test";

thread sendtoSocket (SendMessageW, message);

sendtoSocket.join();
}

The compiler now has to figure out which SendMessageW overload it has to call for thread sendtoSocket (SendMessageW, message);, the asker's or the Win32 API function, and it can't. This causes the compiler to look for additional thread constructors and misleading diagnostics are produced.

Looking into what's up with that, we need an even simpler MCVE where there are no overloads for the templated function

void A(int )
{
}

void A(double )
{
}

template<typename FUNC, typename... ARGS>
void test(FUNC&& func, ARGS&&... args)
{
func(args...);
}

int main()
{
int message = 10;
test(A, message);
}

This results in meaningful diagnostics:

error C2672: 'test': no matching overloaded function found

error C2783: 'void test(FUNC &&,ARGS &&...)': could not deduce template argument for 'FUNC'

What is the purpose of Looper and how to use it?

What is Looper?

Looper is a class which is used to execute the Messages(Runnables) in a queue. Normal threads have no such queue, e.g. simple thread does not have any queue. It executes once and after method execution finishes, the thread will not run another Message(Runnable).

Where we can use Looper class?

If someone wants to execute multiple messages(Runnables) then he should use the Looper class which is responsible for creating a queue in the thread.
For example, while writing an application that downloads files from the internet, we can use Looper class to put files to be downloaded in the queue.

How it works?

There is prepare() method to prepare the Looper. Then you can use loop() method to create a message loop in the current thread and now your Looper is ready to execute the requests in the queue until you quit the loop.

Here is the code by which you can prepare the Looper.

class LooperThread extends Thread {
public Handler mHandler;

@Override
public void run() {
Looper.prepare();

mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// process incoming messages here
}
};

Looper.loop();
}
}

Can't create handler inside thread that has not called Looper.prepare()

You're calling it from a worker thread. You need to call Toast.makeText() (and most other functions dealing with the UI) from within the main thread. You could use a handler, for example.

Look up Communicating with the UI Thread in the documentation. In a nutshell:

// Set this up in the UI thread.

mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message message) {
// This is where you do your work in the UI thread.
// Your worker tells you in the message what to do.
}
};

void workerThread() {
// And this is how you call it from the worker thread:
Message message = mHandler.obtainMessage(command, parameter);
message.sendToTarget();
}

Other options:

You could use Activity.runOnUiThread(). Straightforward if you have an Activity:

@WorkerThread
void workerThread() {
myActivity.runOnUiThread(() -> {
// This is where your UI code goes.
}
}

You could also post to the main looper. This works great if all you have is a Context.

@WorkerThread
void workerThread() {
ContextCompat.getMainExecutor(context).execute(() -> {
// This is where your UI code goes.
}
}

Deprecated:

You could use an AsyncTask, that works well for most things running in the background. It has hooks that you can call to indicate the progress, and when it's done.

It's convenient, but can leak contexts if not used correctly. It's been officially deprecated, and you shouldn't use it anymore.



Related Topics



Leave a reply



Submit