Welcome to async tasks, threads, pools, and executors. Oh, my! So many aspects of threading and thread management that are Android-specific. In her talk from 360|AnDev, Stacy Devino covers the essentials of the various schools of thought on these topics.
Why Learn Multithreading? (0:44)
I consider multithreading a core skill of the modern developer. Even when we’re talking about the phones in our pockets, for the most part, they’re quad core. Lots of RAM, and faster than your computer in 2005!
We basically need the ability to run multiple tasks concurrently. As humans, we can move both of our arms independently, and both of our legs independently. You are able to accomplish different tasks with them.
To demonstrate what you are asking your phone to do on a continuous basis, I want you (yes, the you who is reading this) to try and pat your head and rub your stomach at the same time.
It’s really hard, isn’t it?
When we think about the concepts of multithreaded programming, we have to think about the fact that we have essentially asked our phones to pat their head and rub their stomach at the same time. Fortunately for us, they’re computers, not people, so we can make them do our bidding.
What is a Thread? (2:01)
A thread is an independent execution worker. It is the worker bee. You are the queen bee, and you get to tell the worker bee what to do.
All of Android works on the basis of threads, even when we’re talking about a Hello World app. A Hello World app primarily runs in what we consider a single thread, your main thread. You’ll also see people call it their “UI thread,” although main thread is probably the best construct.
You need to know how to do threading if you want to:
- Do anything within logins
- Load more information
- Cache information
- Use web and RESTful APIs
- Perform tasks in the background that doesn’t cause your app to stall
4 Basic Thread Types (3:20)
Android has four basic types of threads. You’ll see other documentation talk about even more, but we’re going to focus on Thread
, Handler
, AsyncTask
, and something called HandlerThread
. You may have heard HandlerThread just called the “Handler/Looper combo”.
As long as you know how one of these four categories works, you can understand what’s actually going on in the background for things like Futures
, IntentService
, Jobs
, and Alarms
.
Basic Thread (4:31)
So let’s talk about a basic Thread
example.
long rootRandom =
System.currentTimeMillis();
private class RandomThread extends Thread {
long seed;
RandomThread (long seed) {
this.seed = seed;
}
@Override
public void run() {
Random seededRandom = new Random(seed);
rootRandom = seededRandom.nextInt();
}
}
We have something where we want to compute what I’m calling a rootRandom
, like a seededRandom
value. If you do something like encryption, this would be a direct analog.
If I want to make this run in the background, I need to declare all this stuff, and pass in my long integer. It would then compute that seededRandom
value in the background that I would perhaps use somewhere else.
Why would I want to have encryption or a rootRandom
value computed in the background? Because sometimes they’re very, very big. Also, sometimes we don’t think about the fact that an operation that we’re asking or that we actually need for our application, it takes a lot of compute cycles. It can take a lot of values.
If you know Java, this is the basic example of how they generate a thread.
Handler (5:47)
Every so often, I need to send a signal to somewhere else that this process is still alive, or that my app is still running, or that I have a piece of information that’s just kind of timed element, otherwise known as maybe a TimerTask
. But here, we’re actually using it with a Runnable
and a Handler
:
private Handler mHandler = new Handler();
private int lifeSignDelay = 5000;
private Runnable mainRunnable = new Runnable() {
@Override
public void run() {
sendLifeSign(true);
mHandler.postDelayed(mainRunnable, lifeSignDelay);
}
};
Handler
is its own special type of thread in the background. If I want to have things that are executed in services, or in other parts of the application, I can do it really, really simply just by this code.
I’m just declaring a new Handler
, and then I assign some random number, right? Then I have a Runnable
that I then execute in the Handler
. When I’ve actually sent my lifeSign
, I just post delay it. That means that I can have it continually run and self-propagate, but it also means that I’m allowing other things to run without interfering with it. I’m also not holding the objects in that thread for any longer than I need to. It will generate as necessary, so that’s really, really useful.
Knowing about Handler
, we can do a lot of things that allow us to have communication with UI and main threads. Plus, it’s really good with stuff like messaging tasks.
Async Task (7:49)
AsyncTask
can only be called from an Activity on your main thread. That sucks, because sometimes you have things like Services, or other external classes that you want to have actually be able to run separated tasks. However, there are some specific reasons why they made this helper thread.
This thread helps you accomplish things without having to know a lot about what’s actually happening in the background. Let’s assume that I have made a new Activity
, and I wanted to load an image into an ImageView
. But I still want the user to be able to scroll up and down while I’m loading that task. So, what would be a good thing for me to use is AsyncTask
:
public class MyUIActivity extends AppCompatActivity {
//....
private ImageView mImageView = view.findViewById(R.id.someView);
//OnCreate and all other tasks contained above
//Usage
//- inside of a function of from a onClick
new DownloadImageTask().execute("http://example.com/image.png");
//AsyncTask
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}
protected void onPostExecute(Bitmap result) {
mImageView.setImageBitmap(result);
}
}
}
Here I’ve just declared a task that extends AsyncTask
, and then I just execute it and run it in the background. Once it’s done being able to load that information, it puts it right back in the image. If I had those elements, the user can still interact with my app. I’m still prioritizing how I’m actually loading information, but I’m not interfering with what they’re doing.
This would be good for something like user logins, since that’s not something that needs to be used in other activities. If you have an object that is directly related to what is going on in that Activity
, AsyncTask
is a very good tool to use.
HandlerThread (9:35)
Here we have HandlerThread
. This is a relatively new thing that they put out, and there’s not a lot of great documentation on it. If we just want to be able to make something run in the background, we can do what I call the “basic version”:
HandlerThread handlerThread = new HandlerThread("newHandlerThread");
handlerThread.start();
Handler myHandler = new Handler(handlerThread.getLooper());
myHandler.post(new Runnable() {...});
We declare a new HandlerThread
and give it a name. We tell it to start, but then we have to give it a Looper
task.
What does Looper actually do? We’re generating a thread that we’re giving a task, but we’re also saying, “Hey, I want this thread to stay alive.” Maybe I’m actually executing multiple image cachings: I don’t want that thread to die, because every time I generate a new thread, that’s more work that has to go on.
However, you must also be careful with this, because you can end up having a thread that never dies. (That could be good or bad.) If you’re not using that thread, you have computation that is continually going on in the background, and you are using system resources. It’s not like it just stays there.
What is it for?
Why would I want to have something like this actually run? Suppose I have a task where I have no idea whether it’s going to take 5 seconds or 50 seconds? dAsyncTask
might not be a good idea, because I might need it as a system resource, like the camera.
Say I am Pokemon GO. If I’m trying to dynamically grab your camera for that AR mode every single time, and I didn’t lock on it while you actually were using the application, it would take about two seconds of loading time in a lot of cases to be able to load and lock on to that resource. This is especially true if you’re using older devices.
Think beyond just cameras, though. Let’s say you have a chat that’s going back and forth like Google Hangouts. I’d want to have a Looper
thread of the instance that I’m currently in to be able to make sure that I’m grabbing those messaging tasks and being able to post those to the front of the application, but also still be able to get those bits of information even if I go to another application. So, you know how you get those notification saying, “Hey, somebody just sent you another message.”
You can still keep that instance going, and still keep that prioritization, without actually affecting what’s immediately in front of them. If you did that AsyncTask
, that wouldn’t be very good, because if they went to another app, the whole thing would lose Context
.
Java 8 and MultiThreading (13:30)
Some new stuff happened in Java 8 that actually affects threading. The big change we are going to focus on is lambda expressions.
My seededRandom
example above took all this code to write it:
private class RandomThread extends Thread {
long seed;
RandomThread (long seed) {
this.seed = seed;
}
@Override
public void run() {
}
}
That’s not even talking about initialization. Now I can take all of that information and just make it a lambda expression:
private Runnable RandomThread = (long seed) -> {
Random seededRandom = new Random(seed);
rootRandom = seededRandom.nextInt();
}
That substantially decreased the amount of code that I actually have to write.
Advanced Threading (14:20)
Let’s see a really basic example of what is called a “self-managed thread”. Traditionally in Android, you’re going to not declare things as regular thread, but as Runnable
. It’s just another variation of the regular thread class, but it’s a more Android-specific version:
private ConnectThread implements Runnable {
@Override
public void run(){
while(!Thread.currentThread().isInterrupted()) {
if(newConnection) {
newConnection = false;
SocketClass.startClient();
new Thread(new TimeOutThread()).start();
}
if(serverReply != null) {
if(serverReply.equals(Constants.ACK)
||serverReply.equals(Constants.NAK)
||serverReply.equals(Constants.STOP)) {
Thread.currentThread().interrupt();
sendCompleteEvent(serverReply);
return;
}
}
}
}
}
Once I’m spawning, I’m keeping myself alive, not with a Looper
, but with the while
statement. This is a manual example a not-particularly-Androidy way to do it. In this example, I’m creating a socket-level connection with the server and getting very simple responses back.
I’m actually spawning a second thread and causing a timeout. I don’t have that thread on here, but you can tell by the name, TimeOutThread
. I have to make sure that if I had a timeout condition, that I can close it and then close it again.
I’m still keeping the thread alive even though I’ve spawned another, waiting to find out my server reply. If I receive a response, I will interrupt the thread, causing a stop in the while
loop, and say, “Okay, this event completed,” and then I’ll just return back.
I’ve created a situation where I can actually start threads from within myself, and then once I know that that task is done, that whole thread closes down, and I’d be able to create self-managed tasks, so that I know what is going on at every moment.
Executors (17:59)
Executors
help us in a huge way. I would not want to do that self-managed, spawning thread manually, or at least to that degree.
It basically executes Runnable
task. Now, when we talk about Runnable
task, we’re literally talking about Runnable
s. We’re not talking about the class thread that we talked about at the very beginning of this presentation. So I can do two things with it. So I can make AsyncTask
thread, and I can spawn them or I can actually have a situation where I’m making them fully synchronous.
Let’s say I have to write this new thread, RunnableTask
, hand it start
:
(new Thread(RunnableTask)).start();
And that’s assuming I have no other variables to pass into it. Now, with Executor
, I can declare a single executor and be able to run multiple tasks on that one executor. That one executor will be able to generate multiple threads for me:
Executor executor = anExecutor();
executor.execute(new RunnableTask());
executor.execute(new NextRunnableTask());
I just want to be able to execute a single thread in one of my activities, or I have a custom view class or a service. And I just really want a direct call. The DirectExecutor
is the most common implementation:
class DirectExecutor implements Executor {
public void execute(Runnable task) {
task.run();
}
}
Here we’re just giving it the simple Runnable
task, and then just telling it to run. This way, they run in the same thread. This is more of a synchronous operation where we’re executing one, then executing the next, then executing a third. Now if I want to be able to generate new threads every single time, I would use the ThreadPerTaskExecutor
:
class ThreadPerTaskExecutor Implements Executor {
public void execute( Runnable task ) {
new Thread(task).start();
}
}
In the execute
function, I’m generating a new thread for each time that I get a new task put into it.
ThreadPoolExecutor (20:31)
Unless you have one or two simple operations, you should probably be using what’s called a ThreadPoolExecutor
.
In ThreadPerTaskExecutor
, I generate a thread, they do their stuff, and then they die. If I constantly have new stuff coming up, other stuff dying, that becomes a lot of work for me to be able to track.
ThreadPoolExecutor
is instantiated very similarly to how we instantiate Executor
normally:
ThreadPoolExecutor mThreadPool =
new ThreadPoolExecutor(
//initial processor pool size
Runtime.getRuntime().availableProcessors(),
//Max processor pool size
Runtime.getRuntime().availableProcessors(),
//Time to Keep Alive
3,
//TimeUnit for Keep Alive
TimeUnit.SECONDS,
//Queue of Runnables
mWorkQueue
);
In the beginning, we need to make sure that we actually create that thread pool. Now I’m putting in some random kind of numbers here as examples, but you may not want to have every processor core be able to be used as part of your thread pool. And you want to be able to reuse that thread pool for as long as you actually need those pieces to go.
You could have a situation where you have an application that has multiple ThreadPoolExecutors
. If I have a thread pool that I need to have dedicated to preloading images, that would be a good reason to have its own thread pool. But if you’re talking about something where I want to be able to constantly access it, I’ve got an API that I’m constantly hitting, or multiple APIs, hey, I might want to keep one ThreadPoolExecutor
just for that purpose. And it might be my general purpose ThreadPoolExecutor
because I always have stuff that’s running. I never really truly need to close it down unless I’m no longer using the application.
public class MyTaskManager {
//...
//Run the Task on the Pool
mThreadPool.execute(
someTask.getRunnable());
//.. Now when done
mThreadPool.shutdown();
}
The most common thing I see happen is they’re like, “Oh, thread pool executors, great. I need to thread pool execute every single thing.” No. No, you don’t.
You need to make sure that you’re using it only in instances where you are going to constantly be needing the workers that are in that container. Once you are done, you need to shut it down. This is why I make sure to put mThreadPool.shutdown()
. When you are done with those tasks, kill those worker threads.
Executor Services (23:44)
This is really related to Futures
, but you can see it’s a very similar to when we’re talking about the standard Executor
, except that I’m doing this on a fixed thread pool, so I don’t have the ability to change some of those parameters:
private class NetworkServce implements Runnable {
private final ServerSocket serverSocket;
private final ExecutorService pool;
public NetworkService(int port, int poolSize) throws IOException {
serverSocket = new ServerSocket(port);
pool = Executors.newFixedThreadPool(poolSize);
}
public void run() { //run the service
try {
for (; ; ) {
//Run on the new ThreadPool
pool.execute(
new Handler(serverSocket.accept())
);
}
} catch (IOException ex) {
// This signals shutdown the pool
pool.shutdown();
}
}
}
class Handler implements Runnable {
private final Socket socket;
Handler(Socket socket) {
this.socket = socket;
}
public void run(){
//read and service request on socket
}
}
This is geared toward specific things. If you’re familiar with JavaScript Promises, that’s the best analog.
MultiThreading Libraries (24:33)
Now, for RxJava and libraries that have their own thread management. (I’m not trying to start a flame war, and I’m not taking sides!)
You may be thinking that you don’t need to know anything about thread management because there are libraries that do it for you, but you need to know how this works, because ultimately if you get in the practice of using libraries that you don’t understand, you are going to make mistakes.
RxJava
First up is RxJava. I commonly see the situation where you have leaked Messages
, and Observables
, and things that are still active, because people didn’t know that they should close it down. It’s not perfect, because it’s taking a general execution model to what might be a very specific problem that you have.
In a self-propagating process where I’m doing extra spawning, I might want to have some things run concurrently and other things run synchronously. It’s going to be a lot harder for you to set that up in RxJava. So what does RxJava actually give us?
If you’re looking at being able to simply pass bits of information from one activity or class to another, RxJava can make it quite easy to gather that and then be able to return back just those objects. It can significantly simplify the writing of modules. For logins, I can write a very short bit of RxJava code to be able to handle login, and I don’t have to write three or four little threads to be able to handle all of the eccentricities oAuth and all these other problems that you’ve had if you’ve tried to actually build your own login service.
It’s also very easy to set up communication between multiple threads. If you have information that needs to be passed back and forth between multiple threads, you don’t have to build it manually.
It’s good if you need to serialize results. You can do that in RxJava without too much work, and you don’t have to think about how the processes are actually operating in your head.
The biggest challenge that a lot of people have is being able to understand when do you need to have concurrent information, and when do you need to have things that are basically in a line.
Retrofit
A lot of us use Retrofit. I use it a lot, actually. It really, really simplifies calls to your RESTful APIs, and it’s a specialty-built thing. It uses RxJava in the backend, but remember that it’s using RxJava in a very specific capacity, and it’s been optimized for that.
It’s very good at handling your RESTful call, giving you back the information, and turning it into an interface so that you don’t really have to know that much about being able to do custom API calls.
Bolts-Android
Bolts-Android has been open sourced by Parse and Facebook. They use it within their application. It’s a good event-based model to be able to work back and forth. It doesn’t have as many advanced features, but if you’re looking to do things that are very simple from a call and execute system, Bolts Android is a very good library because it’s very purpose-built.
Picasso and Glide
Picasso and Glide handle things a little bit differently in terms of threading under the hood, even though they have very similar results in terms of thread performance. They’re both specialty packages built to be image loaders. Glide actually uses thread pool executor and a lot of specialty Handlers on the underside. Depending on what version of Picasso you have, it’s primarily using ThreadPoolExecutor
, and some newer stuff I’ve seen actually uses RxJava in the back end.
Q&A (29:43)
Q: If I were to make a custom image loader, what kind of threads or thread management would I want to be able to put together if I were not using a library?
Stacy: I would use ThreadPoolExecutor
in combination with other threads to accomplish it. I would also make a Runnable
or a new Thread
to handle different tasks. So I’d have the situation where I’d say, Okay, I want to have this thread pool, but here I want to actually download the image.” Once I’m done downloading the image, maybe I do resize, maybe I do something else after that, and have that all run within that single execution. But when I’m done with it, what might I want to do with that thread pool? Stop it, shut it down.
Now, an important thing I didn’t bring up in here is that thread pools don’t immediately shut down. You send a shutdown signal. So when I say shutdown()
, I’m telling it, “Hey, once you’re finished, I need you to shut down.” But what if I have a thread that’s just gone crazy, a thread that I’ve passed something to, and it just doesn’t want to die, a zombie thread? What I can do in that situation is can say, “Hey, after a certain amount of time, do these threads execute” what they need to, and then shut down? If they didn’t, I can tell it to shut down now, right? It’s the difference between when you hit the power button on your computer to say, “I need this off. I just got a million things” of spyware, it won’t go down.”
Q: If I’m making a custom Executor
and passing a bunch of threads to it, what would you recommend is the best way to pass information back to the main thread?
Stacy: So the same way that you saw that I did things in that kinda simplified double spawning thread, right? I want to be able to either set a variable or be able to pass it back to some external class. So you would treat it just like you would passing information back from a standard method or function that you have. For AsyncTask
, I’ve seen some weird stuff where somebody made a blank activity and a bunch of custom constructors around their AsyncTask
system. Don’t do that.
Keep it to the very simplified task that you just need in that one instance. So in your case, you really just want to treat it like you would any other information or any other function, because really, all you’re doing is just running other functions inside of another thread. The variables, you can quite easily pass those back.
Q: What about Loaders?
Stacy: Once you understand how these core four types of threading tools work, it’s very easy to be able to look at those other things like Loaders, and be able to see what it’s doing and what it’s returning back to you.
Q: Is there a separate method to call in an instance where a thread is running out of control as a zombie?
Stacy: Right, so what you would do is basically once you hit your shutdown, you have another piece of code like in a try-catch statement to figure out, “Okay, I need to wait. Have I counted this many times, right? And If I don’t shutdown in a certain amount of time, I need to send what’s called shutdownnow(). That is the power button your threads.
Receive news and updates from Realm straight to your inbox