Creating Android Applications: Acquiring Data
- The Main Thread
- Getting Off the Main Thread
- The AsyncTask
- The IntentService
- Wrapping Up
While the prime directive of this chapter is to teach you how to acquire data from a remote source, this is really just a sneaky way for me to teach you about Android and the main thread. For the sake of simplicity, all the examples in this chapter will deal with downloading and rendering image data. In the next chapter, on adapters and lists, I’ll introduce you to parsing complex data and displaying it to users. Image data, as a general rule, is larger and more cumbersome, so you’ll run into more interesting and demonstrative timing issues in dealing with it.
The Main Thread
The Android operation system has exactly one blessed thread authorized to change anything that will be seen by the user. This alleviates what could be a concurrency nightmare, such as view locations and data changing in one thread while a different one is trying to lay them out onscreen. If only one thread is allowed to touch the user interface, Android can guarantee that nothing vital is changed while it’s measuring views and rendering them to the screen. This has, unfortunately, serious repercussions for how you’ll need to acquire and process data. Let me start with a simple example.
You There, Fetch Me that Data!
Were I to ask you, right now, to download an image and display it to the screen, you’d probably write code that looks a lot like this:
public void onCreate(Bundle extra){ try{ URL url = new URL("http://wanderingoak.net/bridge.png"); HttpURLConnection httpCon = (HttpURLConnection)url.openConnection(); if(httpCon.getResponseCode() != 200) throw new Exception("Failed to connect"); InputStream is = httpCon.getInputStream(); Bitmap bitmap = BitmapFactory.decodeStream(is); ImageView iv = (ImageView)findViewById(R.id.main_image); if(iv!=null) iv.setImageBitmap(bitmap); }catch(Exception e){ Log.e("ImageFetching","Didn't work!",e); } }
This is exactly what I did when initially faced with the same problem. While this code will fetch and display the required bitmap, there is a very sinister issue lurking in the code—namely, the code itself is running on the main thread. Why is this a problem? Consider that there can be only one main thread and that the main thread is the only one that can interact with the screen in any capacity. This means that while the example code is waiting for the network to come back with image data, nothing whatsoever can be rendered to the screen. This image-fetching code will block any action from taking place anywhere on the device. If you hold the main thread hostage, buttons will not be processed, phone calls cannot be answered, and nothing can be drawn to the screen until you release it.
Watchdogs
Given that a simple programmer error (like the one in the example code) could effectively cripple any Android device, Google has gone to great lengths to make sure no single application can control the main thread for any length of time. Hogging too much of the main thread’s time will result in this disastrous dialog screen (Figure 4.1) showing up over your application.
Figure 4.1. What the user sees when you hold the main thread hostage.
This dialog is unaffectionately referred to by developers as an ANR (App Not Responding) crash. Although operations will continue in the background, and the user can press the Wait button to return to whatever’s going on within your application, this is catastrophic for most users, and you should avoid it at all costs.
What Not to Do
What kind of things should you avoid on the main thread?
- Anything involving the network
- Any task requiring a read or write from or to the file system
- Heavy processing of any kind (such as image or movie modification)
- Any task blocking a thread while you wait for something to complete
Excluding this list, there isn’t much left, so, as a general rule, if it doesn’t involve setup or modification of the user interface, don’t do it on the main thread.
When Am I on the Main Thread?
Anytime a method is called from the system (unless explicitly otherwise stated), you can be sure you’re on the main thread. Again, as a general rule, if you’re not in a thread created by you, it’s safe to assume you’re probably on the main one, so be careful.