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
How to Get Preview in Composable Functions That Depend on a View Model
Get Meta Data of Image - Android
Enable C++11 Support on Android
Couldn't Get Connection Factory Client - Fighting with Google Maps
JSONarray Cannot Be Converted to JSONobject Error
How to Scroll Recyclerview Programmatically
Android: Using HTML5 to Determine Geolocation in Webview with JavaScript API
Ionic2 Background Image Is Resized When Keyboard Is Open
Using Android Google Maps V2 with Custom View or Imageview
Firestorepagingadapter Not Receiving Realtime Updates
Parsing Date/Time to Localtimezone
Firebase Servervalue.Timestamp in Java Data Models Objects
How to Bring a Background Task to The Front Below Honeycomb
How to Handle Empty Response Body with Retrofit 2
Attempt to Reopen an Already-Closed Object SQLitedatabase