An important technique in Android Programming is to make potentially long task run asynchronously in your app, avoid blocking the UI thread, so your app feels more responsive. For example, downloading a file from the Internet, loading a media file from the external storage or applying a filter on an image bitmap etc. I will show you how to implement with AsyncTask, simple java threads and java Executor in Part 1.
Let’s say I want to load a bitmap from the internet, and set it to an ImageView , this might be what you’d write at first:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //This is a terrible example, the UI thread is blocked when user click the button button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { try { //DON'T DO THIS!! URL url = new URL("https://www.google.com/images/logos/google_logo_41.png"); URLConnection conn = url.openConnection(); Bitmap bm = BitmapFactory.decodeStream(conn.getInputStream()); imageView.setImageBitmap(bm); } catch (Exception e) { Log.e(TAG, e.getMessage()); } } }); |
The Java way: simple Threads
You can implement it just like any other Java programs, define a Thread that does the job, and run it. Every Java programmer knows how to do that. Remember that if you want to modify the UI, you need to use runOnUiThread()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); .... button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { try { Thread newThread = new Thread(){ @Override public void run() { try { URL url = new URL("https://www.google.com/images/logos/google_logo_41.png"); URLConnection conn = url.openConnection(); final Bitmap bm = BitmapFactory.decodeStream(conn.getInputStream()); runOnUiThread(new Runnable(){ public void run() { imageView.setImageBitmap(bm); }; }); } catch (Exception e) { Log.e(TAG, e.getMessage()); } } }; newThread.start(); } catch (Exception e) { Log.e(TAG, e.getMessage()); } } }); } |
The Java way: Executors
Executors are like managed threads, you can bound the number of running threads. In many ways, it behave just like Threads. You can define a thread pool of size 1, then the Runnables will be executed sequentially.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | //thread pool of size 2, allowing 2 threads running at the same time ExecutorService threadpool = Executors.newFixedThreadPool(2); public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); .... button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { threadpool.execute(new Runnable() { @Override public void run() { try { URL url = new URL("https://www.google.com/images/logos/google_logo_41.png"); URLConnection conn = url.openConnection(); final Bitmap bm = BitmapFactory.decodeStream(conn.getInputStream()); runOnUiThread(new Runnable(){ public void run() { imageView.setImageBitmap(bm); }; }); } catch (Exception e) { Log.e(TAG, e.getMessage()); } } }); } }); } |
The Android way : AsyncTask
AsyncTask is a built in utility in the Android framework, the basic usage is to extend the class and implement your own code in the doInBackground() method, override also the onPostExecute() if you need to run some UI updates base on the returned results.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | //define what the task do public static class ImageDownloader extends AsyncTask<String, Void, Bitmap>{ ImageView imageView; /** constructor */ public ImageDownloader(ImageView imageView){ this.imageView = imageView; } @Override protected Bitmap doInBackground(String... params) { try { URL url = new URL(params[0]); URLConnection conn = url.openConnection(); Bitmap bm = BitmapFactory.decodeStream(conn.getInputStream()); return bm; } catch (Exception e) { Log.e(TAG, e.getMessage()); } return null; } @Override protected void onPostExecute(Bitmap result) { //set the bitmap results to the imageview //this code will be executed in UI thread imageView.setImageBitmap(result); } } |
In your Activity:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); .... button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { try { ImageDownloader task = new ImageDownloader(imageView); task.execute("https://www.google.com/images/logos/google_logo_41.png"); } catch (Exception e) { Log.e(TAG, e.getMessage()); } } }); } |
 Which one should I use?
The 3 methods can all do the job, however there are important differences that you need to know. Java threads are cheap, easy to implement and use. The problem is that if you don’t write extra code the manage the threads, you may easily create dozens of threads, overwhelms the system and the server. Java Executors are a better choice, because it is essentially a thread pool, you can limit the number of running threads at any time.
AsyncTask comes with Android, so it looks like it is the obvious choice. It integrates nicely in your Android code, but there’s a small detail you might have missed, it does not promise you concurrency. In my experience there can only be only 1 AsyncTask running in your app at any given time. In other words, if you fire up multiple AsyncTasks, they will be executed sequentially. That means if you are writing a multi-threaded function, don’t use AsyncTask.
There’s actually more patterns to run code asynchronously, In next part I will talk about Loaders, Service etc. (Still trying to understand better before writing about it) Stay tuned!
2 replies on “Patterns for running Asynchronous code in Android (Part 1)”
“In my experience there can only be only 1 AsyncTask running in your app at any given time”
You can have as many running in parallel as you want. On older devices, that is the default behavior. On newer devices, you typically have to opt into that behavior, using executeOnExecutor() rather than execute().
Cool! thanks for the information. Android is evolving very fast, I haven’t updated this article since I wrote it, looks like some of them is no longer true. Thanks for remainding