Search code examples
javaandroidmultithreadingandroid-handlerandroid-looper

Handler's `handleMessage` returning thread id of main thread instead that of the worker thread


I'm trying to get a hang of multi-threading in Android. My aim is to send data from the main thread to a worker thread.

I have a main activity with the following code in its onCreate-

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ThreadOne threadOne = new ThreadOne();
        threadOne.start();
        threadOneLooper = threadOne.getLooper();

        Message msg = new Message();
        msg.obj = "message.";

        Log.i("id--(threadOneLooper)", String.valueOf(threadOneLooper.getThread().getId()));
        Log.i("id--(Main)", String.valueOf(getMainLooper().getThread().getId()));

        TestHandler mHandler = new TestHandler(threadOneLooper);
        mHandler.handleMessage(msg);
    }

As you can observe, I store a reference of the worker thread's looper in threadOneLooper and then log the id of its associated thread. At the same time I also print the id of the main thread.

Following is the code for my thread -

public class ThreadOne extends Thread{

    @Override
    public void run() {

        if (Looper.myLooper() == null)
            Looper.prepare();

        Log.i("ThreadOne", "run()");

        // Just making sure all approaches give the same result
        Log.i("Id--Thread id 1", String.valueOf(getLooper().getThread().getId()));
        Log.i("Id--Thread id 2", String.valueOf(getId()));

        Looper.loop();
    }

     Looper getLooper() {
        if (Looper.myLooper() != null)
            return Looper.myLooper();
        else
            return null;
    }

And following is the code of my handler -

public class TestHandler extends Handler {

    TestHandler(Looper myLooper) {
        super(myLooper);
    }
    public void handleMessage(Message msg) {

        String txt = (String) msg.obj;
        Log.i("Handler", txt);
        Log.i("Id--(Handler)", String.valueOf(getLooper().getThread().getId()));
    }
}

Now, the problem is that I had assumed that the Log.i("Id--(Handler)", String.valueOf(getLooper().getThread().getId())); statement would log the thread id of ThreadOne since we are passing the thread's looper into the handler. However, the id being logged is of the main thread. Why is that? Is it correct to assume that handleMessage() is being executed on the main thread?


Solution

  • The problem is with the getLooper() method of ThreadOne. The method returns myLooper() associated with the thread that called the method.

    For returning a Looper associated with ThreadOne I'd suggest the following implementation of the class:

    public class ThreadOne extends Thread {
        private Looper mLooper = null;
    
        @Override
        public void run() {
            Looper.prepare();
            mLooper = Looper.myLooper();
    
            Looper.loop();
        }
    
        Looper getLooper() {
            return mLooper;
        }
    }
    

    Note! The ThreadOne's Looper is null until it is running. You cannot get non-null reference to it earlier. You must check if the thread is running before calling the getLooper() method.

    P.S. You might want to reconsider the approach with getting the ThreadOne's Looper. Having a Handler associated with ThreadOne might be enough (see the example of thread).