499

In the viewpoint of running code in the UI thread, is there any difference between:

MainActivity.this.runOnUiThread(new Runnable() {
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

or

MainActivity.this.myView.post(new Runnable() {
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

and

private class BackgroundTask extends AsyncTask<String, Void, Bitmap> {
    protected void onPostExecute(Bitmap result) {
        Log.d("UI thread", "I am the UI thread");
    }
}
MC Emperor
  • 22,334
  • 15
  • 80
  • 130
Luky
  • 5,346
  • 4
  • 17
  • 15
  • To clarify my question: I supposed those code were called from a service thread, typically a listener. I also supposed there is a heavy work to accomplish either in the doInBackground() function of the AsynkTask or in a new Task(...) called before the first two snippet. Anyway the onPostExecute() of the AsyncTask is being put at the end of the event queue, right? – Luky Oct 12 '12 at 00:03

8 Answers8

313

None of those are precisely the same, though they will all have the same net effect.

The difference between the first and the second is that if you happen to be on the main application thread when executing the code, the first one (runOnUiThread()) will execute the Runnable immediately. The second one (post()) always puts the Runnable at the end of the event queue, even if you are already on the main application thread.

The third one, assuming you create and execute an instance of BackgroundTask, will waste a lot of time grabbing a thread out of the thread pool, to execute a default no-op doInBackground(), before eventually doing what amounts to a post(). This is by far the least efficient of the three. Use AsyncTask if you actually have work to do in a background thread, not just for the use of onPostExecute().

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 28
    Also note that `AsyncTask.execute()` requires you to call from the UI thread anyway, which renders this option useless for the use case of simply running code on the UI thread from a background thread unless you move all of your background work into `doInBackground()` and use `AsyncTask` properly. – kabuko Oct 11 '12 at 23:56
  • @kabuko how I can check that I am calling `AsyncTask` from UI thread? – Neil Galiaskarov Dec 16 '16 at 12:52
  • @NeilGaliaskarov This looks like a solid option: http://stackoverflow.com/a/7897562/1839500 – Dick Lucas Apr 24 '17 at 18:16
  • 1
    @NeilGaliaskarov `boolean isUiThread = (Looper.getMainLooper().getThread() == Thread.currentThread());` – ban-geoengineering Jun 13 '17 at 14:47
  • 1
    @NeilGaliaskarov for versions greater than or equals to M use `Looper.getMainLooper().isCurrentThread` – Balu Sangem Feb 12 '18 at 08:04
285

I like the one from HPP comment, it can be used anywhere without any parameter:

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});
Community
  • 1
  • 1
pomber
  • 23,132
  • 10
  • 81
  • 94
65

There is a fourth way using Handler

new Handler().post(new Runnable() {
    @Override
    public void run() {
        // Code here will run in UI thread
    }
});
David
  • 834
  • 1
  • 10
  • 27
vasart
  • 6,692
  • 38
  • 39
  • 65
    You should be careful with this. Because if you create a handler in a non UI thread you will post messages to the non UI Thread. A handler by default post message to the thread where it is created. – lujop Mar 17 '13 at 18:10
  • 115
    to execute on the main UI thread do `new Handler(Looper.getMainLooper()).post(r)`, which is the preferred manner as `Looper.getMainLooper()` makes a static call to main, whereas `postOnUiThread()` must have an instance of `MainActivity` in scope. – HPP Oct 02 '13 at 06:55
  • 1
    @HPP I didn't know this method, will be a great way when you don't have nor Activity neither a View. Works great! thanks you veryy veryy much! – Sulfkain Apr 09 '14 at 12:58
  • @lujop Same is the case with onPreExecute call back method of AsyncTask. – Sreekanth Karumanaghat Jun 06 '18 at 10:43
18

The answer by Pomber is acceptable, however I'm not a big fan of creating new objects repeatedly. The best solutions are always the ones that try to mitigate memory hog. Yes, there is auto garbage collection but memory conservation in a mobile device falls within the confines of best practice. The code below updates a TextView in a service.

TextViewUpdater textViewUpdater = new TextViewUpdater();
Handler textViewUpdaterHandler = new Handler(Looper.getMainLooper());
private class TextViewUpdater implements Runnable{
    private String txt;
    @Override
    public void run() {
        searchResultTextView.setText(txt);
    }
    public void setText(String txt){
        this.txt = txt;
    }

}

It can be used from anywhere like this:

textViewUpdater.setText("Hello");
        textViewUpdaterHandler.post(textViewUpdater);
Hummus
  • 559
  • 1
  • 9
  • 21
Joe
  • 181
  • 1
  • 2
  • 9
    +1 for mentioning GC and object creation. HOWEVER, I don't necessarily agree with `The best solutions are always the ones that try to mitigate memory hog`. There are many other criteria for `best`, and this has a slight smell of `premature optimization`. That is, unless you know you are calling it enough that number of objects created is an issue (compared to the ten thousand other ways in which your app is probably creating garbage.), what may be `best` is to write the simplest (easiest to understand) code, and move on to some other task. – ToolmakerSteve Jun 22 '15 at 23:48
  • 2
    BTW, `textViewUpdaterHandler` would be better named something like `uiHandler` or `mainHandler`, since it is generally useful for any post to main UI thread; it is not at all tied to your TextViewUpdater class. I would move it away from the rest of that code, and make it clear that it can be used elsewhere... The rest of the code is dubious, because to avoid creating one object dynamically, you break what could be a single call into two steps `setText` and `post`, that rely on a long-lived object that you use as a temporary. Unnecessary complexity, and not thread-safe. Not easy to maintain. – ToolmakerSteve Jun 23 '15 at 00:06
  • If you **really** have a situation where this is called so many times that it is worth caching `uiHandler` and `textViewUpdater`, then improve your class by changing to `public void setText(String txt, Handler uiHandler)` and adding method line `uiHandler.post(this);` Then caller can do in one step: `textViewUpdater.setText("Hello", uiHandler);`. Then in future, if needs to be thread safe, method can wrap its statements inside a lock on `uiHandler`, and caller remains unchanged. – ToolmakerSteve Jun 23 '15 at 00:16
  • I'm pretty sure that you can only run a Runnable once. Which absolutely break you idea, which is nice in overall. – Nativ Apr 24 '16 at 15:36
  • @Nativ Nope, Runnable can be run multiple times. A Thread can't. – Zbyszek Nov 24 '16 at 23:07
13

As of Android P you can use getMainExecutor():

getMainExecutor().execute(new Runnable() {
  @Override public void run() {
    // Code will run on the main thread
  }
});

From the Android developer docs:

Return an Executor that will run enqueued tasks on the main thread associated with this context. This is the thread used to dispatch calls to application components (activities, services, etc).

From the CommonsBlog:

You can call getMainExecutor() on Context to get an Executor that will execute its jobs on the main application thread. There are other ways of accomplishing this, using Looper and a custom Executor implementation, but this is simpler.

Dick Lucas
  • 12,289
  • 14
  • 49
  • 76
12

If you need to use in Fragment you should use

private Context context;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        this.context = context;
    }


    ((MainActivity)context).runOnUiThread(new Runnable() {
        public void run() {
            Log.d("UI thread", "I am the UI thread");
        }
    });

instead of

getActivity().runOnUiThread(new Runnable() {
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

Because There will be null pointer exception in some situation like pager fragment

Ucdemir
  • 2,852
  • 2
  • 26
  • 44
8

use Handler

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        // Code here will run in UI thread
    }
});
humble_wolf
  • 1,497
  • 19
  • 26
raghu kambaa
  • 115
  • 1
  • 6
  • Is there any reason why you chose to use the general purpose Handler over runOnUiThread? – ThePartyTurtle Jun 27 '18 at 18:10
  • 1
    This will give a `java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()` if it's not called from the UI thread. – Roc Boronat Dec 03 '18 at 08:17
1

Kotlin version:

Handler(Looper.getMainLooper()).post {
   Toast.makeText(context, "Running on UI(Main) thread.", Toast.LENGTH_LONG).show()
}

Or if you are using Kotlin coroutines: inside coroutine scope add this:

withContext(Dispatchers.Main) {
   Toast.makeText(context, "Running on UI(Main) thread.", Toast.LENGTH_LONG).show()
}
I.Step
  • 613
  • 6
  • 22