When writing applications one thing to always keep in mind is that you should never block the UI thread. This makes for a poor user experience, and on Android you get the dreaded Application Not Responding dialog if there’s no response to user input within a few seconds. Mono For Android exposes many different ways to do asynchronous operations to help avoid this problem. In this article I’ll go over some of the different options you can pick from.
I’ll say up front that this isn’t meant to be a comprehensive list that includes every possible way to do threading in your application. The order I present them also isn’t meant to be ranked with respect to which ones are best. I’m just going to try to present them in a logical order, and try to explain the pros and cons of each along the way.
For the sake of having a common problem to solve in each example, let’s create a simple application that when launched, will attempt to login to a fake service, display a progress dialog as it loads, and then displays an alert once the login is successful. I wouldn’t recommend actually implementing an authentication service that behaves like this, but our service will take a username, ignore it, sleep for 10 seconds, then return:
public class LoginService
{
public void Login(string username)
{
Thread.Sleep(10000);
}
}
Just for the sake of demonstrating the problem, let’s set up the project first doing the login call synchronously.
[Activity(Label = "Thread Demo", MainLauncher = true)]
public class DemoActivity : Activity
{
private LoginService _loginService;
private ProgressDialog _progressDialog;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
_loginService = new LoginService();
_progressDialog = new ProgressDialog(this) { Indeterminate = true };
_progressDialog.SetTitle("Login In Progress");
_progressDialog.SetMessage("Please wait...");
loginSynchronously();
}
private void loginSynchronously()
{
_progressDialog.Show();
_loginService.Login("greg");
onSuccessfulLogin();
}
private void onSuccessfulLogin()
{
_progressDialog.Hide();
new AlertDialog.Builder(this)
.SetTitle("Login Successful")
.SetMessage("Great success!")
.Show();
}
}
If you run this, for those 10 seconds the application will not respond. This is bad, but we knew that, right?
.NET Thread Object
One option is to explicitly spin up a new thread yourself, forcing the execution to happen off the main thread. Since we have the .NET framework to leverage, we can just use the Thread object from System.Threading like you would in any other .NET application.
private void loginWithThread()
{
_progressDialog.Show();
new Thread(new ThreadStart(() =>
{
_loginService.Login("greg");
RunOnUiThread(() => onSuccessfulLogin());
})).Start();
}
Now the service processing will happen off of the UI thread, and the application can remain responsive in the meantime. One key difference you might notice is the use of RunOnUiThread(). The reason for this is that all UI updates must be made from the UI thread (you’ll be greeted by an exception if you forget to do so). Since we’re doing the login in a background thread, we use RunOnUiThread to run the updates back on the UI thread. This approach will work across MonoTouch and Windows Phone 7 as well.
Java Thread Object
While I wouldn’t necessarily recommend doing it this way, I wanted to include this for completeness. Since Mono For Android wraps some of the Java libraries, you technically do have access to Java’s Thread object as well. This could be useful if you’re porting code over from Java, but in general I’d say it’s not worth the overhead of having to manage the thread object in both runtimes. With only a slight modification from the previous example, you can switch it over to use Java’s Thread:
private void loginWithJavaThread()
{
_progressDialog.Show();
new Java.Lang.Thread(() =>
{
_loginService.Login("greg");
RunOnUiThread(() => onSuccessfulLogin());
}).Start();
}
ThreadPool
Now manually spinning up a thread can be useful, but often it’s much easier to let the framework manage that for you. One such option you might be familiar with already in .NET is the ThreadPool. Using this you can simply dispatch the asynchronous calls you want to make and let it handle thread management for you. Here’s what that looks like:
private void loginWithThreadPool()
{
_progressDialog.Show();
ThreadPool.QueueUserWorkItem(state =>
{
_loginService.Login("greg");
RunOnUiThread(() => onSuccessfulLogin());
});
}
Like the .NET Thread object, this approach also works across MonoTouch and Windows Phone 7, but provides a much cleaner way to manage your threads.
AsyncTask
Now let’s jump back to the Android side of things and look at AsyncTask. This is a class in the Android framework for cleanly wrapping up asynchronous operations, and gives you hooks into the lifecycle for updating the UI, reporting progress, etc. The big win with this class is that you don’t need to worry about explicitly calling things on the UI thread since each of the callbacks designed for updating the UI are already running on it. DoInBackground, the method that will contain the actual work you’re trying to do, stays on the background thread and you can publish progress updates from it.
This approach does expose one typical pain point you’ll run into when porting Java code over to C#: unfortunately C# doesn’t support anonymous classes, which are used heavily in Android. Instead, you’ll typically need to pass in anything your task will need via the constructor. In this case the task needs the LoginService as well as a context to use when showing a progress dialog.
public class LoginTask : AsyncTask
{
private ProgressDialog _progressDialog;
private LoginService _loginService;
private Context _context;
public LoginTask(Context context, LoginService loginService)
{
_context = context;
_loginService = loginService;
}
protected override void OnPreExecute()
{
base.OnPreExecute();
_progressDialog = ProgressDialog.Show(_context, "Login In Progress", "Please wait...");
}
protected override Java.Lang.Object DoInBackground(params Java.Lang.Object[] @params)
{
_loginService.Login(@params[0].ToString());
return true;
}
protected override void OnPostExecute(Java.Lang.Object result)
{
base.OnPostExecute(result);
_progressDialog.Hide();
new AlertDialog.Builder(_context)
.SetTitle("Login Successful")
.SetMessage("Great success!")
.Show();
}
}
OnPreExecute() gets called when the task fires up, but before it starts doing the work. Since it executes right on the UI thread, we use it to bring up a progress dialog. DoInBackground() does the real work of the task on a background thread, then once it returns we get the OnPostExecute() callback on the UI thread, where we hide the progress dialog and show the completion message.
To use the task just create a new instance of it, and call its Execute() method.
private void loginWithAsyncTask()
{
new LoginTask(this, _loginService).Execute("greg");
}
Task Parallel Library
.NET 4.0 introduced the Task Parallel Library in the System.Threading.Tasks namespace, and helps make it easier to write asynchronous tasks in your applications. Even better is that it is available to you in your Mono For Android applications. This will be a trivial example of what it can do, but I encourage you to dig in more afterwards if you like this approach.
Using the TPL’s fluent API, we start a new task that performs the login, and then tell it to continue with onSuccessfulLogin() once that task finishes. If you were writing more complicated tasks, the TPL makes it easy to expressively declare how you want the application to behave.
private void loginWithTaskLibrary()
{
_progressDialog.Show();
Task.Factory
.StartNew(() =>
_loginService.Login("greg")
)
.ContinueWith(task =>
RunOnUiThread(() =>
onSuccessfulLogin()
)
);
}
One downside to this approach is that while it is supported by MonoTouch, there is no Windows Phone 7 support at the moment.
Summary
Here we have outlined several possible routes to go down when trying to do work in a background thread within your application. Each has its own set of advantages and disadvantages, so in general I’d say to go with whichever seems like the right tool for the job. I tend to lean towards ThreadPool because it works across platforms, allowing for more code reuse. That said, I still like using AsyncTask for discrete tasks in an Android application, since it provides a nice abstraction for cleanly defining that task. Did I leave out an approach that you like? Leave a comment and let me know!
Sample code for this is available on GitHub.