Search code examples
androidandroid-handler

Why run Handler in main Looper thread


I read that the Android Handler can be used to communicate with the main Looper thread to post messages that will interact with the views(since only main thread can interact with views there)

But I often see code like the example below that run the Handler in the main thread. Why is that a good think to do, it feels unnecessary unless there is a delay like postDelayed(new Runnable()...

Any explanation on this would bee nice

public class TaleDemoActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);

        new Handler().post(new Runnable() {
            @Override
            public void run() {
                new Handler().post(new Runnable() {
                    @Override
                    public void run() {
                        new Tale("Hello runnables!").tell();
                    }
                });
                new Handler().post(new Runnable() {
                    @Override
                    public void run() {
                        new Tale("Hello runnables!").at().tell();
                    }
                });
            }
        });

        new Handler().post(new TestRunnable());
        new Handler().post(new TestStaticRunnable());

        new Handler().post(new TestAbstractRunnable() {
            @Override
            public void run() {
                new Tale("Hello abstract runnable!").tell();
            }
        });
        new Handler().post(new TestInterfaceRunnable() {
            @Override
            public void run() {
                new Tale("Hello test interface runnable!").tell();
            }
        });

        new Tale().at().tell();
    }

    class TestRunnable implements Runnable {
        @Override
        public void run() {
            new Tale("Hello testRunnable!").tell();
        }
    }

    static class TestStaticRunnable implements Runnable {
        @Override
        public void run() {
            new Tale("Hello testStaticRunnable!").tell();
        }
    }

    abstract class TestAbstractRunnable implements Runnable {

    }

    interface TestInterfaceRunnable extends Runnable {

    }
}

Solution

  • Handler is not used just to communicate with the main Looper.

    Looper is a class that loops over a queue of messages and dispatches them, each instance works with one Thread.

    A HandlerThread is a Thread that also has a Looper attached to it.

    The "main" Thread of your application is also a HandlerThread.

    When you instantiate a Handler with the empty constructor, the Handler will be attached to the Looper of the Thread in which you are creating the instance. If, for instance, you create a Thread in onCreate of an Activity, then the Handler will be attached to the main (UI) Thread because the lifecycle callbacks are executed there.

    When you call post(Runnable), the Runnable will be run on the thread the Handler's Looper is attached to. So, if you create a Handler in one of the lifecycle callbacks of your Activity, then no matter which Thread you post the Runnable from, it will always be executed on the main Thread

    This is the most common usage and it is what your code does, but it's not the only one. For instance you could instantiate a HandlerThread, then from the Activity lifecycle callbacks you could post a Runnable that performs very long operation and it will not block your UI because it will be executed on a different Thread.

    Another usage of post(Runnable) (and sometimes a universal dirty fix for weird UI bugs) is to call it from the main thread just with the purpose of delaying the execution until after the whole method had been executed.

    fun onCreate(savedInstanceState: Bundle?) {
        Handler().post {
            // code here is executed after `onCreate` has returned
        }
        // code here is executed before the one posted on the Handler
    }