What Is the Android Uithread (Ui Thread)

What is the Android UiThread (UI thread)

The UIThread is the main thread of execution for your application. This is where most of your application code is run. All of your application components (Activities, Services, ContentProviders, BroadcastReceivers) are created in this thread, and any system calls to those components are performed in this thread.

For instance, let's say your application is a single Activity class. Then all of the lifecycle methods and most of your event handling code is run in this UIThread. These are methods like onCreate, onPause, onDestroy, onClick, etc. Additionally, this is where all of the updates to the UI are made. Anything that causes the UI to be updated or changed HAS to happen on the UI thread.

For more info on your application's Processes and Threads click here.

When you explicitly spawn a new thread to do work in the background, this code is not run on the UIThread. So what happens if this background thread needs to do something that changes the UI? This is what the runOnUiThread is for. Actually you're supposed to use a Handler (see the link below for more info on this). It provides these background threads the ability to execute code that can modify the UI. They do this by putting the UI-modifying code in a Runnable object and passing it to the runOnUiThread method.

For more info on spawning worker threads and updating the UI from them click here

I personally only use the runOnUiThread method in my Instrumentation Tests. Since the test code does not execute in the UIThread, you need to use this method to run code that modifies the UI. So, I use it to inject click and key events into my application. I can then check the state of the application to make sure the correct things happened.

For more info on testing and running code on the UIThread click here

Difference between the main thread and UI thread

Thanks for an exceptionally interesting question.

Turns out, UI and Main threads are not necessarily the same. However, as stated in the documentation you quoted, the distinction is important only in context of some system applications (applications that run as part of OS). Therefore, as long as you don't build a custom ROM or work on customizing Android for phone manufacturers, I wouldn't bother to make any distinction at all.

The long answer:

First of all I found the commit that introduced @MainThread and @UiThread annotations into support library:

commit 774c065affaddf66d4bec1126183435f7c663ab0
Author: Tor Norbye <tnorbye@google.com>
Date: Tue Mar 10 19:12:04 2015 -0700

Add threading annotations

These describe threading requirements for a given method,
or threading promises made to a callback.

Change-Id: I802d2415c5fa60bc687419bc2564762376a5b3ef

The comment doesn't contain any information related to the question, and since I don't have a communication channel to Tor Norbye (sigh), no luck here.

Maybe these annotations are being used in source code of AOSP and we could derive some insights from there? Let's search for usages of either of the annotations in AOSP:

aosp  $ find ./ -name *.java | xargs perl -nle 'print "in file: ".$ARGV."; match: ".$& if m{(\@MainThread|\@UiThread)(?!Test).*}'
aosp $

the above command would find any usage of @MainThread or @UiThread in any .java file in AOSP (not followed by additional Test string). It found nothing. No luck here either.

So we need to go and look for hints in the source of AOSP. I guessed that I could start from Activity#runOnUiThread(Runnable) method:

public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}

nothing particularly interesting here. Let's see how mUiThread member is being initialized:

final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
attachBaseContext(context);

mFragments.attachActivity(this, mContainer, null);

mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
mUiThread = Thread.currentThread();

mMainThread = aThread;

// ... more stuff here ...
}

Jackpot! The last two lines (others omitted because they are irrelevant) are the very first indication that "main" and "ui" threads might indeed be distinct threads.

The notion of "ui" thread is clear from this line mUiThread = Thread.currentThread(); - "ui" thread is the thread on which Activity#attach(<params>) method is being called. So we need to find out what "main" thread is and compare the two.

It looks like the next hint could be found in ActivityThread class. This class is quite a spaghetti, but I think that the interesting parts are where ActivityThread objects are being instantiated.

There are only two places: public static void main(String[]) and public static ActivityThread systemMain().

The sources of these methods:

public static void main(String[] args) {
SamplingProfilerIntegration.start();

// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);

Environment.initForCurrentUser();

// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());

Security.addProvider(new AndroidKeyStoreProvider());

// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);

Process.setArgV0("<pre-initialized>");

Looper.prepareMainLooper();

ActivityThread thread = new ActivityThread();
thread.attach(false);

if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}

if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}

Looper.loop();

throw new RuntimeException("Main thread loop unexpectedly exited");
}

and:

public static ActivityThread systemMain() {
// The system process on low-memory devices do not get to use hardware
// accelerated drawing, since this can add too much overhead to the
// process.
if (!ActivityManager.isHighEndGfx()) {
HardwareRenderer.disable(true);
} else {
HardwareRenderer.enableForegroundTrimming();
}
ActivityThread thread = new ActivityThread();
thread.attach(true);
return thread;
}

Note the different value these methods pass to attach(boolean). For completeness I will post its source as well:

private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
ViewRootImpl.addFirstDrawHandler(new Runnable() {
@Override
public void run() {
ensureJitEnabled();
}
});
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
// Watch for getting close to heap limit.
BinderInternal.addGcWatcher(new Runnable() {
@Override public void run() {
if (!mSomeActivitiesChanged) {
return;
}
Runtime runtime = Runtime.getRuntime();
long dalvikMax = runtime.maxMemory();
long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();
if (dalvikUsed > ((3*dalvikMax)/4)) {
if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024)
+ " total=" + (runtime.totalMemory()/1024)
+ " used=" + (dalvikUsed/1024));
mSomeActivitiesChanged = false;
try {
mgr.releaseSomeActivities(mAppThread);
} catch (RemoteException e) {
}
}
}
});
} else {
// Don't set application object here -- if the system crashes,
// we can't display an alert, we just want to die die die.
android.ddm.DdmHandleAppName.setAppName("system_process",
UserHandle.myUserId());
try {
mInstrumentation = new Instrumentation();
ContextImpl context = ContextImpl.createAppContext(
this, getSystemContext().mPackageInfo);
mInitialApplication = context.mPackageInfo.makeApplication(true, null);
mInitialApplication.onCreate();
} catch (Exception e) {
throw new RuntimeException(
"Unable to instantiate Application():" + e.toString(), e);
}
}

// add dropbox logging to libcore
DropBox.setReporter(new DropBoxReporter());

ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
synchronized (mResourcesManager) {
// We need to apply this change to the resources
// immediately, because upon returning the view
// hierarchy will be informed about it.
if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {
// This actually changed the resources! Tell
// everyone about it.
if (mPendingConfiguration == null ||
mPendingConfiguration.isOtherSeqNewer(newConfig)) {
mPendingConfiguration = newConfig;

sendMessage(H.CONFIGURATION_CHANGED, newConfig);
}
}
}
}
@Override
public void onLowMemory() {
}
@Override
public void onTrimMemory(int level) {
}
});
}

Why there are two means for initializing ActivityThread (which will become the "main" thread of the application)?

I think the following takes place:

Whenever a new application started, public static void main(String[]) method of ActivityThread is being executed. The "main" thread is being initialized there, and all calls to Activity lifecycle methods are being made from that exact thread. In Activity#attach() method (its source was shown above) the system initializes "ui" thread to "this" thread, which is also happens to be the "main" thread. Therefore, for all practical cases "main" thread and "ui" thread are the same.

This is true for all applications, with one exception.

When Android framework is being started for the first time, it too runs as an application, but this application is special (for example: has privileged access). Part of this "specialty" is that it needs a specially configured "main" thread. Since it has already ran through public static void main(String[]) method (just like any other app), its "main" and "ui" threads are being set to the same thread. In order to get "main" thread with special characteristics, system app performs a static call to public static ActivityThread systemMain() and stores the obtained reference. But its "ui" thread is not overridden, therefore "main" and "ui" threads end up being not the same.

What is the difference between these methods for running code on UI Thread?

In Android, a Thread might have one Looper or MessageQueue. Handler is used to send Message or post Runnable to MessageQueue of a Thread, and it must always be associated with a Looper or a MessageQueue of a Thread.

Method 1

new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
// Code here will run in UI thread
}
});

When open an app, Android create a new thread (called main thread or UI thread) with a Looper and MessageQueue, this thread is used to render UI and process input events from users.

The above code is create a Handler and associated with Looper of UI thread, so the runnable will be queued to MessageQueue of UI thread, and will be executed later.

Method 2

new Handler().post(new Runnable() {
@Override
public void run() {
// Code here will run in UI thread
}
});

Creating a Handler and associated with Looper of current thread, there are 3 cases:

  • If this code is executed on UI thread, then the runnable will be queued to MessageQueue of UI thread and will be executed later.
  • If this code is executed on a background thread, if this thread has a Looper, then the runnable will be queued to MessageQueue of background thread and will be executed later.
  • If this code is executed on a background thread and this thread has no Looper, then an exception will be thrown.

Method 3

runOnUiThread(new Runnable() {
@Override
public void run() {
// Code here will run in UI thread
}
});

runOnUiThread is just a utility method of Activity, it used when you want to execute some code on UI thread. The logic behind this method is if current thread is UI thread, then execute it immediately, otherwise used Handler to post a message to MessageQueue of UI thread (like method 1).

Understanding UI thread

There are many answers concerning the case. I will try to answer exactly to the questions.

What happens to these tasks when android application gone to background?

Nothing is interrupted. Handler will post a Message on MessageQueue on the specified time (unless the hosting process is killed).

Will these tasks are processed even in the background?

From the standpoint of Handler: it doesn't know whether the app is in foreground or background. All it knows, is that it should post a Message some time later.

Will the complete UI thread be suspended in background? Or? All the tasks posted to UI thread are suspended?

What happens to UI thread when there are no tasks posted to it after the activity is completely loaded? Will it be suspended, if there are no tasks are in the Looper?

UI thread won't be "suspended" in any case (unless the hosting process is killed). It's just that it will be idle as a result of MessageQueue being empty - thus no job is needed to be done.

As a side note, refrain from performing UI related actions when app goes to background, because the state of view hierarchy won't be saved correctly. Assume you have performed textView.setText("bob") at a time, when app is in background. Now, the state of this TextView won't be saved, because onSaveInstanceState() has already been executed and won't be executed again, thus if process of the app is killed and recreated (i.e. as a consequence of system resources shortage), then the activity will be restored with a state that it had possessed when onSaveInstanceState() was called. Now user won't see "bob".

You can cancel the scheduled event using Handler#removeCallbacks() API.

What is the difference between @UiThread and @MainThread annotation in Android?

@MainThread is the first thread that starts running when you start your application

@UiThread starts from Main Thread for Rendering user Interface

Also from Android Documentation

Note: The @MainThread and the @UiThread annotations are interchangeable so methods calls from either thread type are allowed for these annotations.

https://developer.android.com/tools/debugging/annotations.html#thread-annotations

Understanding the UI thread

  1. onClick() is indeed on the UI thread. Most of what happens in an Activity happens on the UI thread.

  2. onPostExecte() (and its counterpart onPreExecute()) runs on the UI thread as well. The AsyncTask.onPostExecte() documentation clearly states this. AsyncTask was deliberately designed such that developers could update the UI before and after they do background work.

In general, your code will be running on the UI thread unless you explicitly tell it otherwise. Once you create AsyncTasks, Runnables, or Threads, you need to ensure you understand where your code is executing. In an Activity, it is typically safe to assume you are on the UI thread.

Is main thread the same as UI thread?

Looks like it. Quoted from http://android-developers.blogspot.com/2009/05/painless-threading.html: "When an application is launched, the system creates a thread called "main" for the application. The main thread, also called the UI thread...", Official API document.

Android - How does uiThread block executes?

I heard that RunOnUiThread method posts a runnable( the {} block in kotlin) to the handler of the UI thread. Then the UI thread will execute the runnable.run() method when it is not busy. As you know there must be a time when the UI thread us not busy, because it will cause the system to show ANR(applicafion not responding) message if not (And even it throwsExceptions when the UI thread tries to run time-consuming(lagging) jobs like networking :)).

**edit **
Here's a java code that shows you how runOnUiThread works.
Cited from this blog (Korean)

@Override
public final void runOnUiThread(Runnable action)
{
if (Thread.currentThread() !=mUiThread)
{
mHandler.post(action);
}
else
{
action.run();
}
}

P.S. I am not an export in kotlin, but I hope my answer helped you.



Related Topics



Leave a reply



Submit