Basic thread concepts
The UI thread
How to create and use Java threads
Knowing how to run codes in the background is an essential skill for every developer. Making adept use of threads can boost your app’s performance. Users love that. The bar is very high right now on application performance. No one wants to use an app that runs like molasses. Jittery and janky apps are quickly forgotten nowadays.
In this chapter, we’ll explore the wonderful world of Threads.
Basic Concepts
When you launch an app, a process is created, and it’s allocated some resources like memory, among other things. It’s also given one thread.
A thread, loosely speaking, is a sequence of instructions. It’s the thing that executes your code. During the time the app is alive, this thread will utilize the process’ resources. It may read or write data to memory, disk, or sometimes even the network I/O. While the thread is interacting with all these, it is just waiting. It can’t take advantage of CPU cycles while it’s waiting. We can’t let all those CPU cycles go to waste. Can we? We can create other threads so that when one or more threads are waiting for something, the other threads can utilize the CPU. This is the case for multithreaded applications.
When the Android Runtime creates an instance of an app, that process will be given one thread. It’s called the main thread; some developers call it the UI thread, but we’ll be given just the one thread and no more. The good news is we can create more threads. The UI thread is allowed to spawn other threads.
The UI Thread
- 1.
The call is placed in a MessageQueue.
- 2.
There it will stay until a handler picks it up for execution.
- 3.
Then, it gets executed on the main thread.
The main thread is not only used for displaying UI elements, it’s also used for everything else that happens in your app. You may recall that Activity has lifecycle methods like onCreate, onStop, onResume, onCreateOptionsMenu, onOptionsItemSelected, and other methods. Whenever the code runs on these blocks, the runtime cannot process any message in the queue. It’s in a blocked state; a blocked state means that the main thread is waiting for something to finish before it can continue to go about its business—this is not good for user experience.
Most of the time, the calls we make don’t take that much time to run. The calls are cheap, so to speak, in terms of computing resources. So, it’s not really a big deal. It becomes a big deal when we make calls that take quite a while to complete. The Android development team’s guidance is that we should limit our calls to no more than 15 milliseconds; anything more than that, then we should consider running those codes in a background thread. This guidance was from Project Butter, which was released around the time of Jelly Bean (Android 4.1); it was meant to improve Android apps’ performance. When the runtime senses that you’re doing too much on the main thread, it’ll start dropping frames. When you’re not making expensive calls, the app runs at a very smooth 60 frames per second. If you hog the main thread, you will start noticing sluggish performances because the frame rates begin to drop. In the Android world, this is known as jank. Doing too much processing on the main thread will cause slow UI rendering, which results in dropped or skipped frames, which results in the app’s perceived stuttering. The app becomes janky.
Before Project Butter, if the UI thread senses that you’re doing way too much on the UI thread, for example, opening a large file, creating a network socket, or anything that takes a long time, the runtime may slap you with the ANR (Android Not Responding) screen. ANRs are rarely seen nowadays; jank is more the concern.
If you want to avoid jank, you need to know how to run time-consuming or I/O-related codes in the background.
It is merely setting the Text property of a TextView to a calculated field. The calculation isn’t complex.
GCF calculation
Nested calls
Get GitHub info
The Android Runtime will smack you with a NetworkonMainThreadException if you try to run this on the UI thread. Any code that uses network connectivity can’t run in the UI thread.
Java threads, which are from Java
AsyncTask, which is part of the Android framework
Handlers and Messages, which are also part of the Android framework
- 1.
Spawn a background thread when executing long-running or resource-consuming tasks.
- 2.
If you need to do something in the UI thread (like setting the property of a View) while inside a background thread, you have to find a way to go back to the UI thread before working on the View element.
Of all the ways to run codes in the background, the Thread will be the most familiar with Java programmers, but it will also be the most rudimentary.
Threads and Runnables
We can run statements in the background by spawning new threads. Thread objects are created either by extending the java.lang.Thread class or by implementing the Runnable interface. A thread object describes a single thread of execution, and that execution happens inside the Thread class’ run() method.
class Worker
How to create and run a thread
❶ | Create an instance of the Thread class. |
❷ | Invoke the start() method. If you forget this, the thread won’t run. Calling the start() method kickstarts the thread. |
The Worker class implements the Runnable interface
How to use a Runnable object
❶ | Create an instance of the Worker. |
❷ | Create an instance of a Thread class by passing an instance of the Runnable class (the Worker instance) to the Thread’s constructor. |
❸ | Now we can kickstart the thread by calling the start() method. |
fetchUserInfo
❶ | We want to take the GitHub userid as a parameter. |
❷ | Let’s construct a String URL that incorporates the GitHub userid. |
❸ | We’ll use the OkHttpClient from Square, Inc. OkHttpClient is an open source project designed to be an efficient HTTP client. It supports the SPDY protocol. SPDY is the basis for HTTP 2.0 and allows multiple HTTP requests to be multiplexed over one socket connection; you can learn more about SPDY at https://en.wikipedia.org/wiki/SPDY. As of Android 5.0, OkHttpClient is a part of the Android platform and is used for all HTTP calls. This example is lifted from OkHttpClient’s web page at https://square.github.io/okhttp/. |
❹ | Let’s use the try with resources block, so we don’t have to worry about cleaning up later; when the try block goes out of scope, all the resources we opened within that block will be automatically closed. |
❺ | If all our previous setup went well, we could get the response from the GitHub API. |
EditText—Where we can input the GitHub userid.
Button—Which will trigger the user action.
TextView—Where we will display the results of our fetch call from GitHub.
ScrollView—We will wrap the TextView here, so that the displayed text is multiline, and it can scroll.
activity_main.xml
AndroidManifest.xml
Next, let’s deal with the Gradle file. There are two changes we need to make on the app module’s Gradle file. First, we need to add the dependency entry for OkHttpClient. Second, we need to add an entry to enable View binding.
build.gradle (Module:app)
❶ | Insert this block to enable View binding. Setting the viewBinding to true allows View binding for this module. |
❷ | Insert this dependency so we can use OkHttpClient. |
You will need to sync the Gradle file after editing it.
Since Android Studio 3.6, you can already replace findViewById calls with View binding; when it’s enabled for a module, it generates a binding class for each XML layout file that the module contains. Each binding class contains references to the root view and all views that have an ID. The name of the binding class is generated by converting the XML file’s name to camel case and adding the word "Binding" to the end—for example, in our project, we only have activity_main.xml; so, a class called ActivityMainBinding will be generated for us.
MainActivity
❶ | I declared the TextView as a member because we will use this later, outside of the onCreate() method. |
❷ | Call the inflate() method (statically) to create an instance of the binding class for MainActivity to use. |
❸ | Get a reference of the root view by calling getRoot(). |
❹ | Instead of passing the name of the layout file (activity_main), pass the root view instead. |
❺ | Let’s get a reference to the TextView object; this is where we will send the results of the GitHub API call. |
❻ | Let’s set up a click listener for the Button. |
❼ | Get the content of the EditText. |
❽ | Then, instantiate the Thread object and kickstart it. We haven’t defined the Thread object yet; we will do that shortly. Listing 11-13 shows the RunBackground class implemented as an inner class in MainActivity. |
RunBackground inner class
❶ | Let’s extend the Thread class because we want this to run in the background. We’re implementing this also as an inner class to have access to the members of the outer class (MainActivity). |
❷ | Let’s take the GitHub username as a parameter to this class' constructor. |
❸ | Override the run() method. Everything inside this method is what will run in the background. |
❹ | Let’s call the fetchUserInfo() method. |
❺ | The GitHub API will return the result as a String. If you need to work with the returned object as JSON, you need to use JSONObject, as shown here. This way, if you want to extract specific parts of the userInfo, you can make calls like userInfo.getString("id"). |
❻ | While you’re in a background thread, you cannot make calls to any UI element, for example, if you want to set the text of either TextView or an EditText, you won’t be able to do that. To make changes to the UI element while running in a background thread, you have to go back to the UI thread; and the way to do that is by calling the runOnUiThread() method. The runOnUiThread() method takes a Thread object as a parameter; override this Thread object’s run() method, and that is where you write the codes to make changes to the UI (as shown here). |
❼ | Now we can write the results of the GitHub call to the TextView. |
Now, run the application either in an emulator or a device and try to fetch your own GitHub info.
Summary
When you try to do too much on the main thread, the runtime may start dropping frame rates, which results in jank.
The UI thread is responsible for creating the UI elements, among other things. Don’t overburden this thread. If you need to do some time-consuming or resource-consuming tasks, spawn a background thread and do that task there.