Looper and MessageQueue structure

Tutorial: How to use a Handler?


Today I will show you how to benefit from Handlers in Android 🙂

First of all, Handlers aren’t some new concept, they were there long ago. How long? Well, from API level 1. Still, I always felt Android developers don’t use them enough, me included.

When I’ve discover the brilliance of Handlers, I was surprised how handy they are and how easy is to use them.

So, what does a Handler do? A few things, really.

  • Schedule and process messages
  • Schedule and execute Runnables
  • Perform its actions on a different Thread that a Handler was created in
  • Being reused as many times as needed

Pretty nice stuff to begin with. One drawback, is that it creates an ambiguity, because Handler is not a Runnable (which is a type of class with a run code inside) and neither a Thread (independent flow of execution). You can think of a Handler as an advanced mechanism to handle a queue. Doesn’t matter if this queue contains Messages or Runnables, and whether they should be processed on a main or some background thread. Handler was created to handle messages, literally, and this is the main bit to remember.

A good example of using a Handler is when you have a Runnable, you do something in the background thread, and then – you want to update UI at some point. In this case, you initialize a Handler as new Handler(Looper.getMainLooper), call handler.post() and do the UI job inside the post(). Useful, isn’t it?

We all know, that a single AsyncTask can be executed only once. This is not a case with Handlers. There is even a special class to take care of a few Handlers – HandlerThread, which in turn takes care of a Looper, internally, so you don’t have to worry about it all the time. Here you can find a good explanation how it works, as well as see some code.

To show you different kind of Handler, Thread, and Runnable examples, I have created an Android test project – HandlersExample. I’ve pasted the main code part below. It has lots of comments, so everything should be self-explanatory. Run this code and experiment with Handlers, – they’re worth it!

package com.lomza.examples.handlers;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.UiThread;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;

 * Main activity with methods to demonstrates the usage of Handlers, Runnables, and Messages :)
 * @author Antonina Tkachuk
public class MainActivity extends AppCompatActivity {
    private TextView tv01;
    private TextView tv02;
    private TextView tv03;
    private TextView tv04;
    private TextView tv05;
    private TextView tv06;
    private TextView tv07;
    private Button button07;

    private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
    private Handler currentThreadHandler;
    private Handler decorViewHandler;
    private final ChangeTextHandler customHandler = new ChangeTextHandler(this);
    private final Handler notLeakyHandler = new Handler();
    private final Runnable notLeakyRunnable = new ChangeTextRunnable(this, "Hi from leak-safe Runnable!");

    private static final String TAG = "[Handlers]";
    private static final String BUNDLE_KEY = "greeting";

    protected void onCreate(Bundle savedInstanceState) {


    protected void onStart() {


    protected void onStop() {
        // when posting Runnables or Messages, always remember to call removeCallbacks() or removeMessages()
        // or removeCallbacksAndMessages() for both.
        // this ensures that all pending tasks don't execute in vain; for instance, the user has left our activity
        // and he doesn't really care if some job is finished or not, so it's our responsibility to cancel it

        // pass null to remove ALL callbacks and messages
        if (decorViewHandler != null)

    private void initView() {
        tv01 = (TextView) findViewById(R.id.tv_01);
        tv02 = (TextView) findViewById(R.id.tv_02);
        tv03 = (TextView) findViewById(R.id.tv_03);
        tv04 = (TextView) findViewById(R.id.tv_04);
        tv05 = (TextView) findViewById(R.id.tv_05);
        tv06 = (TextView) findViewById(R.id.tv_06);
        tv07 = (TextView) findViewById(R.id.tv_07);
        button07 = (Button) findViewById(R.id.button_07);
        button07.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {

    private void postTaskWithOrdinaryThread() {
        Runnable notForHandlerTask = new Runnable() {
            public void run() {
                // as written here - https://developer.android.com/guide/components/processes-and-threads.html#Threads,
                // do NOT access the Android UI toolkit from outside the UI thread, as sth unexpected may happen
                // for instance, you might get android.view.ViewRootImpl$CalledFromWrongThreadException
                tv01.setText("Hi from Thread(runnable)!");
                // if you call thread.run(), this would be TRUE, as no new Thread would be created
                // read the explanation here - http://stackoverflow.com/a/35264580/655275
                Log.d(TAG, "[postTaskWithOrdinaryThread] Current looper is a main thread (UI) looper: "
                        + (Looper.myLooper() == Looper.getMainLooper()));
        Thread thread = new Thread(notForHandlerTask);

    private void postTaskWithHandlerOnMainThread() {
        Runnable mainThreadTask = new Runnable() {
            public void run() {
                // since we use Looper.getMainLooper(), we can safely update the UI from here
                tv02.setText("Hi from Handler(Looper.getMainLooper()) post!");
                Log.d(TAG, "[postTaskWithHandlerOnMainThread] Current looper is a main thread (UI) looper: "
                        + (Looper.myLooper() == Looper.getMainLooper()));

    private void postTaskWithHandlerOnCurrentThread() {
        currentThreadHandler = new Handler();
        Runnable currentThreadTask = new Runnable() {
            public void run() {
                // since we use current thread (and from onCreate(), it's the UI thread), we can safely update the UI from here
                tv03.setText("Hi from Handler() post!");
                Log.d(TAG, "[postTaskWithHandlerOnCurrentThread] Current looper is a main thread (UI) looper: "
                        + (Looper.myLooper() == Looper.getMainLooper()));


    private void postTaskInsideBackgroundTask() {
        Thread backgroundThread = new Thread(new Runnable() {
            public void run() {
                // pretend to do something "background-y"
                try {
                } catch (InterruptedException e) {

                mainThreadHandler.post(new Runnable() {
                    public void run() {
                        tv04.setText("Hi from a Handler inside of a background Thread!");


    private void postTaskWithThisWindowAndTextViewHandlers() {
        // this line will return null from onCreate() (and even if called from onResume()) and cause NPE when trying to post();
        // this is because the handler isn't attached to the view if it's not fully visible
        decorViewHandler = getWindow().getDecorView().getHandler();
        Runnable decorViewTask = new Runnable() {
            public void run() {
                // View's post() uses UI handler internally
                tv07.post(new Runnable() {
                    public void run() {
                        tv07.setText("Hi from getWindow().getDecorView().getHandler() > TextView.post()!");
                        Log.d(TAG, "[postTaskWithThisWindowAndTextViewHandlers] Current looper is a main thread (UI) looper: "
                                + (Looper.myLooper() == Looper.getMainLooper()));

    private void postTaskWithHandlerOnBackgroundThread() {
        final Runnable pretendsToBeSomeOtherTask = new Runnable() {
            public void run() {
                Log.d(TAG, "[postTaskWithHandlerOnBackgroundThread] Is there a looper? " + (Looper.myLooper() != null));
                // you'll get java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
                // this is because Handlers need to have a Looper associated with them; when the task was run on the main thread,
                // main thread looper was used, but if we call this from the background thread, there is NO looper to use
                // read more - https://developer.android.com/reference/android/os/Looper.html

        final Thread thread = new Thread(pretendsToBeSomeOtherTask);

    private void postTaskWithNotLeakyHandlerAndRunnable() {
        // in order to eliminate leaks, both Handler and Runnable should be static
        // static inner classes do not hold an implicit reference to the outer class
        // it seems like a lot of useless work, but it's the most accurate and bug-free way
        // read more - http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html
        notLeakyHandler.postDelayed(notLeakyRunnable, 500);

    private static class ChangeTextRunnable implements Runnable {
        private final WeakReference activity;
        private final String greetingMessage;

        public ChangeTextRunnable(MainActivity activity, String greetingMessage) {
            this.activity = new WeakReference<>(activity);
            this.greetingMessage = greetingMessage;

        public void run() {
            if (greetingMessage == null) {
                Log.e(TAG, "The message is null ChangeTextRunnable.run()!");

            MainActivity activity = this.activity.get();
            if (activity == null) {
                Log.e(TAG, "Activity is null ChangeTextRunnable.run()!");


    private void sendMessageToChangeTextHandler() {
        Message messageToSend = customHandler.obtainMessage();
        Bundle bundle = new Bundle();
        bundle.putString(BUNDLE_KEY, "Hi from custom inner Handler!");
        messageToSend.what = 6;

    private static class ChangeTextHandler extends Handler {
        private final WeakReference activity;

        public ChangeTextHandler(MainActivity activity) {
            this.activity = new WeakReference<>(activity);

        public void handleMessage(Message msg) {
            MainActivity activity = this.activity.get();
            if (activity == null) {
                Log.e(TAG, "Activity is null ChangeTextHandler.handleMessage()!");

            final String text = (String) msg.getData().get(BUNDLE_KEY);
            if (!TextUtils.isEmpty(text)) {
                switch (msg.what) {
                    case 6:

Thanks for reading and, as always, leave your comments below 😉

Useful links

Communicating with the UI Thread

How to Leak a Context: Handlers & Inner Classes

Activitys, Threads, & Memory Leaks

Android background processing with Handlers, AsyncTask and Loaders – Tutorial

I would like to thank Oliver Mueller, as I have used the image from his wonderful blog post in the header of this post.

Like and share:

Published by

Tonia Tkachuk

I'm an Android Developer, writing code for living and for fun. Love beautiful apps with a clean code inside. Enjoy travelling and reading.

271 thoughts on “Tutorial: How to use a Handler?”

  1. Hi Tonia, very neat your explanation about handlers, thanks.
    But I keep asking myself, why we can’t find easily methods to calculate time of a thread (handlers) execution? Do you know some? I would like to benchmark some hpc for android.

  2. You are a lifesaver. And a real beauty, too.

    I’m trying to create an app with internet connectivity. Of course I need to implement multithreading to accomplish this, but it’s easier said than done. This is exact what I needed.

  3. Pingback: cialis 20mg
  4. Pingback: viagra online
  5. Pingback: natural viagra
  6. Pingback: viagra pills
  7. Pingback: cheap sildenafil
  8. Pingback: cialis
  9. Pingback: canadian pharmacy
  10. Pingback: cialis
  11. Pingback: viagra
  12. Pingback: buy levitra online
  13. Pingback: buy cialis usa
  14. Pingback: buy cialis canada
  15. Pingback: Testing60
  16. Pingback: cialis soft tablet
  17. Pingback: qkbpimip
  18. Pingback: purchase cialis
  19. Pingback: viagra prices
  20. Pingback: amoxicillin 100
  21. Pingback: furosemide cream
  22. Pingback: ventolin prices
  23. Pingback: doxycycline.
  24. Pingback: neurontin action
  25. Pingback: reviews on paxil
  26. Pingback: plaquenil coupon
  27. Pingback: Google
  28. Pingback: safe buy cialis
  29. Pingback: Zakhar Berkut hd
  30. Pingback: amoxil 500 mg
  31. Pingback: generic furosemide
  32. Pingback: quineprox 50
  33. Pingback: prednisone 2
  34. Pingback: priligy usa
  35. Pingback: lasix 10 mg price
  36. Pingback: daily cialis
  37. Pingback: ivermectin 50 mg
  38. Pingback: zithromax 250
  39. Pingback: meritking
  40. Pingback: elexusbet
  41. Pingback: eurocasino
  42. Pingback: madridbet
  43. Pingback: meritroyalbet
  44. Pingback: eurocasino
  45. Pingback: eurocasino
  46. Pingback: meritking
  47. Pingback: meritroyalbet
  48. Pingback: cialis commercial
  49. Pingback: meritroyalbet
  50. Pingback: goodrx sildenafil
  51. Pingback: sildenafil 25 mg
  52. Pingback: tadalafil cialis
  53. Pingback: Google
  54. Pingback: cialis 80mg
  55. Pingback: mulnupiravir
  56. Pingback: buy zanaflex uk
  57. Pingback: zanaflex canada
  58. Pingback: Anonymous
  59. Pingback: baricitinib fda
  60. Pingback: tamoxifen gel
  61. Pingback: Anonymous
  62. Pingback: Anonymous
  63. Pingback: ivermectin cost
  64. Pingback: order ivermectin
  65. Pingback: ivermectin generic
  66. Pingback: ivermectin 3
  67. Pingback: viagra men sale
  68. Pingback: tadala
  69. Pingback: free trial cialis
  70. Pingback: stromectol uk buy
  71. Pingback: cialis tabletas
  72. Pingback: buy cialis jelly
  73. Pingback: ivermectin dose
  74. Pingback: cheap cialis india
  75. Pingback: merck pill
  76. Pingback: cialis pills
  77. Pingback: child porn
  78. Pingback: sildenafil uses
  79. Pingback: stromectol ebay
  80. Pingback: ivermectin wormer
  81. Pingback: cefixime
  82. Pingback: research tadalafil
  83. Pingback: cialis cost 20mg
  84. Pingback: cialis walmart
  85. Pingback: stromectol tablets
  86. Pingback: ivermectin 0.1
  87. Pingback: stromectol generic
  88. Pingback: azithromycin heart
  89. Pingback: online ivermectin
  90. Pingback: cephalexin
  91. Pingback: 500mg azithromycin
  92. Pingback: rulide
  93. Pingback: augmentin gen
  94. Pingback: meritking
  95. Pingback: meritking
  96. Pingback: meritroyalbet
  97. Pingback: lasix 500 mg price
  98. Pingback: trcasino
  99. Pingback: eurocasino
  100. Pingback: eurocasino
  101. Pingback: buy ivermectin nz
  102. Pingback: ivermectin tablets
  103. Pingback: tadalafil dosage
  104. Pingback: child porn
  105. Pingback: playluckylands
  106. Pingback: cheap stromectol
  107. Pingback: stromectol xl
  108. Pingback: ivermectin online
  109. Pingback: stromectol otc
  110. Pingback: child porn
  111. Pingback: stromectol uk buy
  112. Pingback: cialis otc
  113. Pingback: mazhor4sezon
  114. Pingback: Gz92uNNH
  115. Pingback: do-posle-psihologa
  116. Pingback: bahis siteleri
  117. Pingback: JXNhGmmt
  118. Pingback: 2illicit
  119. Pingback: 1suavity

Leave a Reply

Your email address will not be published.