I’ve had the same question you’ve had, so I’m gonna risk an answer.
I believe it is better to start with a very important concept on Android, which is the Looper
.
Looper
The Looper
is a class that is used to handle messages using queue. That is, when a Thread
want to receive messages from multiple threads and perform processing thread-safe, is a Looper
to be used.
The Looper
, together with the Handler
basically transforms the Thread
chain (I believe this is done using the method Thread.currentThread
hopefully) on a pipeline, where messages are added to this pipeline, processed by Thread
and released.1
This code snippet can help:
public class MyThread extends Thread {
@Override
public void run() {
try {
// Prepara o looper na thread corrente
// a thread corrente vai ser detectada implicitamente
Looper.prepare();
// Agora, vamos automaticamente ligar o Handler
// ao Looper que foi ligado a thread corrente
// Você não precisar especificar o Looper explicitamente, ele saberá
handler = new Handler();
// Depois dessa linha, a Thread ira iniciar de fato
// Rodando o loop de mensagens (Looper) e nao ira
// terminar o loop ate que um problema ocorra ou
// voce chame o metodo quit() do Looper
Looper.loop();
} catch (Throwable t) {
Log.e(TAG, "halted due to an error", t);
}
}
}
Soon to handle the messages sent to that Thread
, you can send a Runnable
:
handler.post(new Runnable() {
@Override
public void run() {
// Isso sera feito no pipeline da Thread ligada a esse Handler
}
});
Or post a message using obtainMessage
and using the method sendMessage
. Something like:
Message msg = handler.obtainMessage(what);
// Popular mensagem com dados
handler.sendMessage(msg);
// ou
msg.setTarget(handler);
msg.sendToTarget();
Of course you will have to implement a Handler
overloading the method handleMessage
to access the messages sent.
Well, that part I hope has been understood, now I will go to the part concerning the answer.
UI Thread or Main Thread
I believe you’ve heard enough, this is it Thread
that Android performs all operations regarding the graphical interface and other related application features. Be modifying/inflating the layout, managing Activities
(Which includes handling of touch events), Fragments
, Services
, ContentProviders
and BroadcastReceivers
2. Why access to these components should be made in Main Thread
, because all this processing is not Thread-Safe
, having a very large cost and risks to be (imagine if it happens a deadlock ?!?).
I think this one StackTrace
that I generated can help visualize this:
java.lang.Arithmeticexception: divide by zero
at android.app.Activitythread.performLaunchActivity(Activitythread.java:2198)
at android.app.Activitythread.handleLaunchActivity(Activitythread.java:2257)
at android.app.Activitythread.access$800(Activitythread.java:139)
at android.app.Activitythread$H.handleMessage(Activitythread.java:1210)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.Activitythread.main(Activitythread.java:5086)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.Internal.os.Zygoteinit$Methodandargscaller.run(Zygoteinit.java:785)
at com.android.Internal.os.Zygoteinit.main(Zygoteinit.java:601)
at Dalvik.system.Nativestart.main(Native Method)
Caused by: java.lang.Arithmeticexception: divide by zero
at br.com.Planning.poker.app.activity.Customdeckactivity.onCreate(Customdeckactivity.java:82)
at android.app.Activity.performCreate(Activity.java:5248)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1110)
at android.app.Activitythread.performLaunchActivity(Activitythread.java:2162)
at android.app.Activitythread.handleLaunchActivity(Activitythread.java:2257)
at android.app.Activitythread.access$800(Activitythread.java:139)
at android.app.Activitythread$H.handleMessage(Activitythread.java:1210)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.Activitythread.main(Activitythread.java:5086)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.Internal.os.Zygoteinit$Methodandargscaller.run(Zygoteinit.java:785)
at com.android.Internal.os.Zygoteinit.main(Zygoteinit.java:601)
at Dalvik.system.Nativestart.main(Native Method)
You can see that to start/create my Activity
(that generated this exception), he needed to send a message to the Looper
of Main Thread
(which is initiated by Zygote
), to be sued.
Getting to the heart of your doubt, this code will not run outside the Main Thread
*, precisely because you are sending a message to Main Thread
, for IT to process, which will stop other processing (making your application not responsive to touch, generating ANR).
*I believe his background is Main Thread
.
There may be other ways to do it but I always do it that way:
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
new AsynctTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
// Processamento fora da Main Thread
return null;
}
@Override
protected void onPostExecute(Void result) {
// Processamento na Main Thread (podendo alterar a UI)
}
}.execute();
}
}, delay);
Regarding processing outside the Main Thread
, there are also the Loaders
that greatly facilitate the form of access to the local database and the notification of data availability. A tutorial I used to learn was: http://www.androiddesignpatterns.com/2012/07/loaders-and-loadermanager-background.html. It is simpler and more than the officer, but it does not replace of course.
Thread Sleep vs Handler
Beyond the approach using Handler
which is undoubtedly more advantageous, there is no loss of performance using Handler
in Main Thread
because the Looper
has already been initialized. The class builder Handler
seeks an instance of Looper
in the ThreadLocal
, soon there is no creation of the queues and nothing extra because that Looper
is created by Zygote
.
In the approach of AsyncTask
there is a relatively high cost of creating a Thread
just to use Thread.sleep
and then run a processing.
If processing is done on background
in the solution by Handler
there is also the cost of creating a AsyncTask
, but the enforcement order of Threads
on Android.
Before Android 1.6, all the escalation of Threads
was serial. Indeed the Thread.sleep
causes problems in this scenario by "locking" the schedule.3
Between Android 1.6 and 2.3, the escalation became parallel. Logo o Thread.sleep
wouldn’t cause problems.3
In android 3.0 the default scheduling was again serial (due to problems in parallelism), but it was introduced a way to scale in parallel using Executors
, being a way to overcome the serial problem.3
Configuration change
There is a recurring problem in the use of AsyncTask
and the life cycle of Activity
/Fragment
. The problem occurs when starting a AsyncTask
and while its processing does not end happens the destruction or stop of the Activity
. Soon when updating the UI at the end, it is not the same Activity
and consequently memory Leak may occur. This post helps to visualize the problem and a solution: http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
For this it is recommended the use of Loaders
, which are automatically managed by virtue of the Activity
.
I hope it was clear, even though I didn’t know how much you knew about these details, I tried to do the most comprehensive so that other people with this same doubt can take advantage.
Any detail is just say that I complement my answer.
References:
- http://mindtherobot.com/blog/159/android-guts-intro-to-loopers-and-handlers/
- https://stackoverflow.com/questions/3652560/what-is-the-android-uithread-ui-thread
- http://www.jayway.com/2012/11/28/is-androids-asynctask-executing-tasks-serially-or-concurrently/
The right is to use Handler anyway. Do not use Sleep in Android applications.
– rochasdv