Sei sulla pagina 1di 17

11/3/2016

Android:Looper,Handler,HandlerThread.PartI.

HOME

SUBSCRIBE

Android: Looper, Handler,


HandlerThread. Part I.
11 OCTOBER 2014 on android, thread, looper

What do you know about threads in Android? You may say "I've
used AsyncTask to run tasks in background". Nice, but what else?
"Oh, I heard something about Handlers , because I used them to
https://blog.nikitaog.me/2014/10/11/androidlooperhandlerhandlerthreadi/

1/17

11/3/2016

Android:Looper,Handler,HandlerThread.PartI.

show toasts from background thread or to post tasks with delay".


That's denitely better, but in this post I'll show what's under
the hood.
Let's start from looking at the well-known AsyncTask class, I bet
every Android developer has faced it. First of all, I would like to
say that you can nd a good overview of AsyncTask class at the
ocial documentation. It's a nice and handy class for running
tasks in background if you don't want to waste your time on
learning how to manage Android threads. The only important
thing you should know here is that only one method of this class
is running on another thread - doInBackground . The other methods
are running on UI thread. Here is a typical use of AsyncTask :

//MyActivity.java
publicclassMyActivityextendsActivity{

@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
MyAsyncTaskmyTask=newMyAsyncTask(this);
myTask.execute("http://developer.android.com");
}
}

//MyAsyncTask.java
publicclassMyAsyncTaskextendsAsyncTask<String,Void,
Integer>{

https://blog.nikitaog.me/2014/10/11/androidlooperhandlerhandlerthreadi/

2/17

11/3/2016

Android:Looper,Handler,HandlerThread.PartI.

privateContextmContext;

publicMyAsyncTask(Contextcontext){
mContext=context.getApplicationContext();
}

@Override
protectedvoidonPreExecute(){
Toast.makeText(mContext,"Let'sstart!",
Toast.LENGTH_LONG).show();
}

@Override
protectedIntegerdoInBackground(String...params){
HttpURLConnectionconnection;
try{
connection=(HttpURLConnection)new
URL(params[0])
.openConnection();
returnconnection.getResponseCode();
}catch(IOExceptione){
e.printStackTrace();
}
return1;
}

@Override
protectedvoidonPostExecute(Integerinteger){
if(integer!=1){
Toast.makeText(mContext,"Gotthefollowingcode:
"+integer,
https://blog.nikitaog.me/2014/10/11/androidlooperhandlerhandlerthreadi/

3/17

11/3/2016

Android:Looper,Handler,HandlerThread.PartI.

Toast.LENGTH_LONG).show();
}
}
}

We will use the following straightforward main layout with


progress bar for our test:

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/progressBar"/>
</LinearLayout>

If progress bar freezes, we are doing heavy job on the UI thread.


We are using AsyncTask here, because it takes some time to get a
response from server and we don't want our UI to be blocked
while waiting this response, so we delegate this network task to
another thread. There are a lot of posts on why using AsyncTask is
bad (if it is an inner class of your Activity / Fragment , it holds an
implicit reference to it, which is bad practice, because
Activity / Fragment can be destroyed on conguration change, but
https://blog.nikitaog.me/2014/10/11/androidlooperhandlerhandlerthreadi/

4/17

11/3/2016

Android:Looper,Handler,HandlerThread.PartI.

they will be kept in memory while worker thread is alive; if it is


declared as standalone or static inner class and you are using
reference to a Context to update views, you should always check
whether it is null or not). All tasks on UI thread (which drives
the user interface event loop) are executed in sequential manner,
because it makes code more predictable - you are not falling into
pitfall of concurrent changes from multiple threads, so if some
task is running too long, you'll get ANR (Application Not
Responding) warning. AsyncTask is one-shot task, so it cannot be
reused by calling execute method on the same instance once
again - you should create another instance for a new job.
The interesting part here is that if you try to show a toast from
doInBackground method you'll get an error, something like this:

java.lang.RuntimeException:Can'tcreatehandlerinside
threadthathasnotcalledLooper.prepare()
atandroid.os.Handler.<init>(Handler.java:121)
atandroid.widget.Toast$TN.<init>(Toast.java:322)
atandroid.widget.Toast.<init>(Toast.java:91)
atandroid.widget.Toast.makeText(Toast.java:238)
at
com.example.testapp.MyActivity$MyAsyncTask.doInBackground(MyA
ctivity.java:25)
at
com.example.testapp.MyActivity$MyAsyncTask.doInBackground(MyA
ctivity.java:21)

https://blog.nikitaog.me/2014/10/11/androidlooperhandlerhandlerthreadi/

5/17

11/3/2016

Android:Looper,Handler,HandlerThread.PartI.

Why did we face this error? The simple answer: because Toast
can be shown only from UI thread, the correct answer: because it
can be shown only from thread with Looper . You may ask "What
is a Looper ?". Ok, it's time to dig deeper. AsyncTask is a nice class,
but what if the functionality it has is not enough for your needs?
If we take a look under the hood of AsyncTask , we will nd, that
actually it is a box with tightly coupled components: Handler ,
Runnable , Thread . Let's work with this zoo. Each of you is familiar

with threads in Java , but in Android you may nd one more class
HandlerThread derived from Thread . The only signicant
dierence between HandlerThread and Thread you should turn your
attention to is that the rst one incorporates Looper, Thread and
MessageQueue. Looper is a worker, that serves a MessageQueue for
a current thread. MessageQueue is a queue that has tasks called
messages which should be processed. Looper loops through this
queue and sends messages to corresponding handlers to process.
Any thread can have only one unique Looper , this constraint is
achieved by using a concept of ThreadLocal storage. The bundle
of Looper + MessageQueue is like a pipeline with boxes.

https://blog.nikitaog.me/2014/10/11/androidlooperhandlerhandlerthreadi/

6/17

11/3/2016

Android:Looper,Handler,HandlerThread.PartI.

You may ask "What's the need in all this complexity, if tasks will
be processed by their creators - Handlers ?". There are at least 2
advantages:
As mentioned above, it helps you to avoid race conditions,
when concurrent threads can make changes and when
sequential execution is desired.
Thread cannot be reused after the job is completed while

thread with Looper is kept alive by Looper until you call quit
method, so you don't need to create a new instance each time
you want to run a job in background.
You can make a Thread with Looper on your own if you want, but I
recommend you to use HandlerThread (Google decided to call it
https://blog.nikitaog.me/2014/10/11/androidlooperhandlerhandlerthreadi/

7/17

11/3/2016

Android:Looper,Handler,HandlerThread.PartI.

HandlerThread instead of LooperThread ): it already has built-in


Looper and all pre-setup job will be done for you. And what's

about Handler ? It is a class with 2 basic functions: post tasks to


the MessageQueue and process them. By default, Handler is
implicitly associated with thread it was instantiated from via
Looper , but you can tie it to another thread by explicitly providing

its Looper at the constructor call as well. Now it's time to put all
pieces of theory together and look on real example. Let's imagine
we have an Activity and we want to post tasks (in this article
tasks are represented by the Runnable interface, what they
actually are will be shown in next part) to its MessageQueue (all
Activities and Fragments are living on UI thread), but they should

be executed with some delay:

publicclassMyActivityextendsActivity{

privateHandlermUiHandler=newHandler();

@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

ThreadmyThread=newThread(newRunnable(){
@Override
publicvoidrun(){
for(inti=0;i<4;i++){
try{
TimeUnit.SECONDS.sleep(2);
}catch(InterruptedExceptione){
https://blog.nikitaog.me/2014/10/11/androidlooperhandlerhandlerthreadi/

8/17

11/3/2016

Android:Looper,Handler,HandlerThread.PartI.

e.printStackTrace();
}
if(i==2){
mUiHandler.post(newRunnable(){
@Override
publicvoidrun(){

Toast.makeText(MyActivity.this,
"Iamatthemiddle
ofbackgroundtask",
Toast.LENGTH_LONG)
.show();
}
});
}
}
mUiHandler.post(newRunnable(){
@Override
publicvoidrun(){
Toast.makeText(MyActivity.this,
"Backgroundtaskis
completed",
Toast.LENGTH_LONG)
.show();
}
});
}
});
myThread.start();
}
}
https://blog.nikitaog.me/2014/10/11/androidlooperhandlerhandlerthreadi/

9/17

11/3/2016

Android:Looper,Handler,HandlerThread.PartI.

Since mUiHandler is tied up to UI thread (it gets UI thread Looper


at the default constructor call) and it is a class member, we have
an access to it from inner anonymous classes, and therefore can
post tasks to UI thread. We are using Thread in example above
and its instance cannot be reused if we want to post a new task,
we should create a new one. Is there another solution? Yes, we
can use a thread with Looper . Here is a slightly modied previous
example with HandlerThread instead of Thread , which
demonstrates its ability to be reused:

//MyActivity.java
publicclassMyActivityextendsActivity{

privateHandlermUiHandler=newHandler();
privateMyWorkerThreadmWorkerThread;

@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

mWorkerThread=newMyWorkerThread("myWorkerThread");
Runnabletask=newRunnable(){
@Override
publicvoidrun(){
for(inti=0;i<4;i++){
try{
TimeUnit.SECONDS.sleep(2);
}catch(InterruptedExceptione){
https://blog.nikitaog.me/2014/10/11/androidlooperhandlerhandlerthreadi/

10/17

11/3/2016

Android:Looper,Handler,HandlerThread.PartI.

e.printStackTrace();
}
if(i==2){
mUiHandler.post(newRunnable(){
@Override
publicvoidrun(){

Toast.makeText(MyActivity.this,
"Iamatthemiddle
ofbackgroundtask",
Toast.LENGTH_LONG)
.show();
}
});
}
}
mUiHandler.post(newRunnable(){
@Override
publicvoidrun(){
Toast.makeText(MyActivity.this,
"Backgroundtaskis
completed",
Toast.LENGTH_LONG)
.show();
}
});
}
};
mWorkerThread.start();
mWorkerThread.prepareHandler();
mWorkerThread.postTask(task);
https://blog.nikitaog.me/2014/10/11/androidlooperhandlerhandlerthreadi/

11/17

11/3/2016

Android:Looper,Handler,HandlerThread.PartI.

mWorkerThread.postTask(task);
}

@Override
protectedvoidonDestroy(){
mWorkerThread.quit();
super.onDestroy();
}
}

//MyWorkerThread.java
publicclassMyWorkerThreadextendsHandlerThread{

privateHandlermWorkerHandler;

publicMyWorkerThread(Stringname){
super(name);
}

publicvoidpostTask(Runnabletask){
mWorkerHandler.post(task);
}

publicvoidprepareHandler(){
mWorkerHandler=newHandler(getLooper());
}
}

I used HandlerThread in this example, because I don't want to


manage Looper by myself, HandlerThread takes care of it. Once we
https://blog.nikitaog.me/2014/10/11/androidlooperhandlerhandlerthreadi/

12/17

11/3/2016

Android:Looper,Handler,HandlerThread.PartI.

started HandlerThread we can post tasks to it at any time, but


remember to call quit when you want to stop HandlerThread .
mWorkerHandler is tied to MyWorkerThread by specifying its Looper .

You cannot initialize mWorkerHandler at the HandlerThread


constructor call, because getLooper will return null since thread
is not alive yet. Sometimes you can nd the following handler
initialization technique:

privateclassMyWorkerThreadextendsHandlerThread{

privateHandlermWorkerHandler;

publicMyWorkerThread(Stringname){
super(name);
}

@Override
protectedvoidonLooperPrepared(){
mWorkerHandler=newHandler(getLooper());
}

publicvoidpostTask(Runnabletask){
mWorkerHandler.post(task);
}
}

Sometimes it will work ne, but sometimes you'll get NPE at the
postTask call stating, that mWorkerHandler is null . Surpise!

https://blog.nikitaog.me/2014/10/11/androidlooperhandlerhandlerthreadi/

13/17

11/3/2016

Android:Looper,Handler,HandlerThread.PartI.

Why does it happen? The trick here is in native call needed for
new thread creation. If we take a loop on piece of code, where
onLooperPrepared is called, we will nd the following fragment in

the HandlerThread class:

publicvoidrun(){
mTid=Process.myTid();
Looper.prepare();
synchronized(this){
mLooper=Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid=1;
}

The trick here is that run method will be called only after new
thread is created and started. And this call can sometimes happen
after your call to the postTask method (you can check it by
https://blog.nikitaog.me/2014/10/11/androidlooperhandlerhandlerthreadi/

14/17

11/3/2016

Android:Looper,Handler,HandlerThread.PartI.

yourself, just place breakpoints inside postTask and


onLooperPrepared methods and take a look which one will be hit

rst), so you can be a victim of race conditions between two


threads (main and background). In the next part what these tasks
really are inside MessageQueue will be shown.

Nikita

Share this post

Read more posts by this author.

Moscow

http://blog.nikitaog.me/about

https://blog.nikitaog.me/2014/10/11/androidlooperhandlerhandlerthreadi/

15/17

11/3/2016

Android:Looper,Handler,HandlerThread.PartI.

6Comments

DeveloperNotes

Recommend 5

Share

Login

SortbyBest

Jointhediscussion
phreakhead2monthsago

"NPEatthepostTaskcallstating,thatmWorkerHandlerisnull"
Okyesbuthowdoyoufixit?

Reply Share
metrophobe3monthsago

Justtoadd....theDIAGRAMisPRICELESS!

Reply Share
metrophobe3monthsago

Beautifullyexplainedsir!

Reply Share
blackkara8monthsago

Veryuseful,thanks

Reply Share
AlexanderSanchez8monthsago

ThatprepareLooper()bitisveryimportant.Thanks!

Reply Share
VinothVino8monthsago

Nicearticle

Reply Share

ALSOONDEVELOPERNOTES

Android:Looper,Handler,
HandlerThread.PartI.

Android:Looper,Handler,
HandlerThread.PartII.

16comments2yearsago

15comments2yearsago

NikitaOgorodnikovHiOman!Calling

NikitaOgorodnikovthanksforthe

start()inconstructorisnotasafe
approach,youcantakealookonthis
question

question!Thepurposeofthe`Looper`
isdescribedinthe1stparthere
http://blog.nikitaog.me/2014/1...,itis

Subscribe d AddDisqustoyoursiteAddDisqusAdd

https://blog.nikitaog.me/2014/10/11/androidlooperhandlerhandlerthreadi/

Privacy

16/17

11/3/2016

Android:Looper,Handler,HandlerThread.PartI.

Developer Notes 2016

https://blog.nikitaog.me/2014/10/11/androidlooperhandlerhandlerthreadi/

Proudly published with Ghost

17/17

Potrebbero piacerti anche