Hey guys, welcome to Coding in Flow! In this video we will learn how we canexecute code on a separate thread in Android.
This is oneof the less interesting topics and there are a lot of convenienceclasses and external libraries that abstract that behaviour for us sowe don't have care of it But it's important to learn about theseconcepts nevertheless So you should really not avoid it.
Nowas you might know when we start an Android app we have onesingle thread by default which is called the “Main Thread”.
Anothername for it is the “UI Thread” because it's responsible for managingand updating the user interface Let's say for example we click a button.
Then the button gets set into it's “pressed” state whichchanges its appearance like this then the onClick() method is triggeredand then the button gets redrawn to its default state.
All of this happens sequentially one by one.
But it happens so quicklythat we don't notice any delay Now the problem is what if we click thisbutton and then execute some long-running task before the button canbe set back to its default state? Let's simulate this behavior.
For thisI have prepared the layout you just saw.
And these two buttons hereSTART and STOP call this startThread() and stopThread()methods which right now are empty.
So let's sayin the startThread() method we want to do some heavy operations.
In this example we will just freeze the thread for 10 seconds.
And wedo this in a for-loop “for (int i = 0; i < 10; " we want tofreeze it for 10 seconds "i++)" to increase it with everyiteration And in here we call "Thread.
sleep()” forone second each or 1000 milliseconds.
Now this couldthrow an exception so we have to click on this light bulbhere and click on “Surround with try/catch” and then weget rid of this warning This way we can simulate heavyoperations by just letting this thread freeze for 10 seconds.
And alsoin each round let's show a log message so we see that it's actually running.
For this I first create a TAG with “logt” like this (which isa live template by the way) I press [Enter] and it automaticallycreates this TAG with “MainActivity” as a String.
Now inthe first line of this for-loop right here I write “logd”which is another live template [Enter] And it created this log messagewhich has the method name in the string here.
Outside ofthe string I write ” + i” to see in which round we arecurrently So let's run it So here is our app and here is Logcat.
And when I click this button it stays in the pressed state.
Ouroperation is running and I can't change the switch herebecause the app is completely frozen And now we get this “Thread Example”which is the name of our app “isn't responding”.
And the same happensfor any other long running operation on the Main Thread.
So insteadwe have to put all this work into a background thread which is not ashard as you might expect There are two ways of startinga background thread Either by extending the Thread class andoverriding its run() method or by implementing the Runnableinterface and then passing this Runnable object to a new thread Thread and Runnable are core Javaclasses.
So they are not limited to Android.
But there are some Androidspecific classes like Handler and Looper which make running thisthreads and communication between different threads moreconvenient.
And these classes on the other hand build a basis forhigher abstraction classes like AsyncTask, HandlerThread orThreadPoolExecutor But don't get turned off by all thesedifferent names we will take a look at the basics inthis video Okay, so as I said there are twodifferent ways to start a background thread and we will start byextending the “Thread” class So I create an inner class which I call”ExampleThread” “extends Thread”.
And in here we canoverride the run() method where we define our background work.
So what do we want to do in the background? We want to do all ofthis what we previously did directly in startThread() which isthis whole for-loop We cut it out We delete the “super” part andpaste it in Now instead of hardcoding our 10 secondsin here we can also give this Thread a constructor For this we first create a variable “int seconds”.
And then we createa constructor “ExampleThread()” where we pass”int seconds” as an argument “this.
seconds = seconds” And now instead of “10” we pass”seconds” here And now in our startThread() buttonmethod here we create an instance of our ExampleThread.
We call it”thread = new ExampleThread()” and we have to pass a number for theseconds, which is “10” in our example And then we just call “thread.
start()”to execute it So let's run it again Now when I click this button we still doour work, but this time our UI is still working The other way of starting our backgroundwork is by implementing the Runnable interface.
So again wecreate an inner class We call it “ExampleRunnable”.
And thistime we don't write “extends” we write “implements” because it'san Interface.
“Runnable” Now we get a warning because we have tooverride this run() method And here we want to do exactly the same We want to execute this for-loop And the same as in our thread we createa member variable for the seconds In a constructor Like this So now in our button method instead of”ExampleThread thread” we create an “ExampleRunnable runnable =new ExampleRunnable()” Again, we pass 10.
But in order toexecute this Runnable on a separate thread we still have tocreate a Thread object So we write “new Thread()”.
And here wepass our “runnable” as an argument .
start() Let's test it again We click START And it works as well Actually in both cases we executea Runnable because the Thread class implements the Runnable interface.
Thisis why It has this run() method as well.
The Runnable objectsbasically just encapsulates the code that we want to have executedby a thread Now even though the thread approachseems more straightforward and more logical the preferred way isusually implementing the Runnable interface and then startingit on a new thread Because we don't actually want to changethe behaviour of the Thread class We just want to pass it some work toexecute.
Also this way we loosely couple the work that is to dowith the choice of concurrency because a Runnable can still be executedon the Main Thread if we decide to do that later.
Then wecould just call “runnable.
run()” to execute it directlyon the thread we are on the moment which is the UI thread in this example.
However, both approaches work Okay and now let's say when we are atfive seconds we want to update something on the UI thread.
Instead ofadding another View let's just create a variable for ourbutton and change the text of this button.
So I create a “private Button”variable which I call “buttonStartThread”.
And I assign it inonCreate() “buttonStartThread = findViewById(R.
“And I already gave it an id “button_start_thread);” And now in our run() method here of ourRunnable when we are at five seconds “if (i == 5)” then we want to changethe text of the button buttonStartThread.
setText(“50%”); Now let's run it again START.
It starts counting And at five seconds our app crashes Let's take a look the Logcaterror mesage We have a CalledFromWrongThreadExceptionwhich says “Only the original thread that createda view hierarchy can touch its views” And this pretty much already explainswhat is wrong Since these UI widgets are notthread-safe we can't access them from another thread than the UI thread.
Thisis why we get this crash And the same is the case for any otherView, like TextViews or ImageViews.
We can only access themfrom the UI thread So let's go back into our code To execute this setText() method onthe UI thread we need a so called Handler.
So let's go allthe way up to the top of our MainActivity and create a Handlervariable here “private Handler” this one withthe “android.
os” package name We call it “mainHandler = newHandler();” This is not a core Java class.
This isan Android class which makes it more convenient to passwork to be done between different threads Now by default a thread starts, executessome work and then it finishes Now lets think about what would happen ifthis would be the case for our Main Thread.
We would start ourapp, it would show the first screen and then it wouldfinish.
So our program would be over basically.
This wouldnot work So instead the Thread has a so called”message queue” and a “looper” which loops through thismessage queue and keeps the thread alive this way.
This message queue contains packages of work to be done.
Soeverything we do like changing a switch or clicking a button gets put into thismessage queue and then executed sequentially.
The looperloops through this message queue and this way the thread is kept aliveand doesn't just finish when its work is done And one responsibility of this Handlerclass is getting work into this message queue.
And by defaultit's associated with the thread where we instantiate it.
Sowhen we write “new Handler” here at the beginning of our MainActivitythis gets executed on the Main Thread so this Handler will only work withthe message queue of the Main Thread Which means that we can use it to getour work to the Main Thread We could also create a message queue and a looper for the thread we created.
This way we could keep italive and feed it with more work But we only want to execute this once, so we don't need a message queue here Now, I know that is all can be a bitconfusing.
And I will put a link to a very good video intothe description box below by the Android developer team where theyexplain this in more detail and with some niceimages.
So make sure to watch this video as well.
But the point in ourexample is that we can use this Handler which we created onthe Main Thread “mainHandler” to get work from ourbackground thread here into the UI thread by calling “.
post()” And here we have to pass a “Runnable” aswell which we create with “new Runnable”.
So if you remembera Runnable itself doesn't start a new thread.
Instead it'sbasically just a package of work to be done.
We close it downhere with a semicolon And in here we write”buttonStartThread.
setText()” and set it to the same text as before Now this will work because the Handlerposts it to the UI thread So let's test it again We click START and at five we should seeour new button text And this works.
Perfect Now, instead of creating this Handler onthe UI thread we can also create it here and justpass the looper of the UI thread This is why I called this “mainHandler”, to see the difference So we can write “Handler” We call it “threadHandler = ” then againwe write “new Handler()” And then if we don't pass anything herelike this and try to use this Handler to post this Runnable thiswon't work.
Let's test it START And we get a crash “Can't create a handler inside threadthat has not called Looper.
prepare()” because a Handler needs a Looper and amessage queue to work with to feed it with packages of work.
Wedidn't create such a Looper and message queue in our backgroundthread.
But the handler we created here is tied to this background thread.
Instead we want to associate this handler with the Main Thread.
Andwe can simply do this by passing the UI looper to the constructor.
And we can get it with “Looper.
And now thishandler will be associated with the looper of the UI thread.
Solet's run it again Now it should not crash START And at five our button shouldchange again And this works But of course there is a more convenientway to do this so you don't have to create this handlerexplicitly For this I'm gonna put a commentaround this Like this.
And instead we can take ourbutton variable and call “.
post()” on here directly and passa “new Runnable” And we use this post() method the sameas we used our Handler.
post() method because internally it does the same.
It's just a convenience method of the View class.
And our buttonis a View A TextView or an ImageView is alsoa View.
So you can call post() on them as well And there is a third way Comment around this as well We can also just call “runOnUIThread()”where we pass a Runnable as well runOnUIThread() is an Activity method.
This is why we can call it directly here without any variablebefore it And in here we can do the same These approaches work as well.
And I leave them as comments in here so you can take a look at code inthe description box But as you can see all this updatingand running the background thread can be a bit confusing especially whenour app grows And to make this more convenient thereare classes in place that make it easier like the AsyncTask whichabstracts this functionality And I already have a video on AsyncTaskwhich you can find when you click on a little ⓘ inthe top right corner of this video So make sure to watch this as well Now, instead of creating a separateclass which implements Runnable we can also use anonymous inner classeswhich require a bit less code So now I'm gonna put a comment aroundthis part in our startThread() button method And instead we can just write “new Thread()”, pass a “new Runnable”, define our work in here.
Just gonnawrite “//work” And down here where this red line is wewrite “.
start()” to start the thread And if you want to stop a thread beforeit's finished you can do this with a boolean variable So we would write “private”.
Now I'mgonna write “volatile” which is a Java keyword.
Thisbasically makes sure that all our threads always access the most up todate version of this variable and not a cached version “boolean”.
We call it “stopThread” andinitially it's set to “false” Now, we don't have set this to “false”explicitly because this is the default value, but I just do it forclearness And then in our for-loop we can checkfor this variable.
So we write “if (stopThread)” is true then we want to call “return” to leavethis run() method which will stop this thread because its work is done So let's go into our button methods once again We want to execute our “runnable” threadand not the anonymous one so I'm gonna put a comment around this And in stopThread() we simply want toset this boolean flag to “true” stopThread = true; Which also means that when we start ourthread we have to set it to “false” And now let's run it again START.
It's running Let's see if it still updates our buttonproperly It does.
STOP and it stops.
We can startit again It starts.
Okay, so I hope this video helpedyou a bit Don't worry if you are confused.
This istotally normal because it's a complicated topic.
Just watch itagain a couple of times and make sure to watch the video inthe description box as well which explains Thread, Message queue andLooper in more detail And don't forget to subscribe tothe channel for more Android tutorials Take care.