When an android application is launched, a thread for that application is created by the system. This thread is called “main” or “UI thread”. Android uses single thread model ie. the system does not create a separate thread for each instance of a component. When you perform a long running task like database access, your UI thread can yield poor performance and these tasks will block the whole UI. Hence you can always create separate threads called worker threads to run your long running tasks. All the UI updations should be done from the UI thread. Follow the below two rules while dealing with Android threads.
- Do not block the UI thread
- Do not access the Android UI toolkit from outside the UI thread.
Lets us now discuss how to make your application execute time consuming tasks without blocking the UI thread
Threading is used when your program executes time consuming tasks(database access, calling web services etc.) with out blocking the UI thread. You can get away with blocking the UI thread using the following,
- Thread with handlers
- AsynTask
Creating a thread
The threads other than the Main UI thread are called as Worker threads. If you have operations to perform, that takes long time to complete, It is recommened to include them in worker threads. The worker threads are created using the below code snippet.
new Thread(new Runnable() { public void run() { //time consuming tasks } }).start();
You can write the code of your long running tasks inside the run() callback. lets assume, I have an ImageView in my layout file. I want to download an image from the internet and upload that to the ImageView. I can add the code for downloading the image inside run() callback. Once the image from the internet is downloaded, I should set that image to the ImageView. Now the question is, Where will you write the code to update that ImageView? Writing the code to update the ImageView inside run() method would not yield guaranteed result since you violet the second rule that we have already discussed, which says “Do not access the Android UI toolkit from outside the UI thread”. This can be solved by using handlers.
Handlers
Handlers are used to make updations to the UI in response to messages sent by threads running within the application’s process. Each handler instance is associated with the thread from which its been created and that thread’s message queue. Handlers are implemented in the main thread of an application. Handlers are used to send and process the following that are associated with the thread’s messagequeue. When a thread sends a message to the Handler it will get saved into a message queue and gets executed by the UI thread as soon as possible.
- Messages
- Runnable Objects
Let us look at the code snippet that tells how you can communicate with the handler using messages and Runnable Objects.
Communicating with the handler using Messages
This is done using the callback method handleMessage(Message msg) which is called when the messages are sent by the thread using the following methods
- sendEmptyMessage(int)
- sendMessage(Message)
Message sending can be scheduled and delayed by using the below methods,
- sendEmptyMessageAtTime(int,long)
- sendEmptyMessageDelayed(int,long)
- sendMessageAtTime(Message,long)
- sendMessageDelayed(Message,long)
Handler object is created and the run callback is used as below
Handler handler = new Handler() { //handles the thread's messages and updates the UI @Override public void handleMessage(Message msg) { //code to update the UI goes here.... } };
The code that is used to update the user interface should be written inside the handleMessage() method which is called when the thread sends any messages to this handler. The worker thread which sends an empty message is created as below.
@Override public void onClick(View v) { // TODO Auto-generated method stub //starting a new thread on click of a button new Thread(new Runnable() { public void run() { try { /*long running tasks code is written here... I have made the thread to sleep for 4 seconds*/ Thread.sleep(4000); //Sending an empty message to the handler indicating that the task has finished its execution handler.sendEmptyMessage(0); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }).start(); }
As you can notice in the above code I have simply made the thread to sleep for 4 seconds so as to create an environment where the main thread has to wait for 4 seconds to get response from the worker thread. In your examples you can do any time consuming tasks like database access etc. in the place of this line. In the above snippet I have created a thread on click of a button which sends an empty message to the handler. Then the handler processes that message and updates the UI. Here I have just used a textview whose text changes when a thread sends the message to the handler.
Handler handler = new Handler() { @Override public void handleMessage(Message msg) { txt.setText("text updated by worker thread"); } };
In the above snippet, our worker thread has just sent an empty message to the handler. What if our thread wants to send any data to the handler? This is done using the method Message.setData(Bundle bundle), where Bundle object contains the data to be transfered. The code snippet for data transfer between thread and handler is as below.
@Override public void onClick(View v) { // TODO Auto-generated method stub //starting a new thread on click of a button new Thread(new Runnable() { public void run() { try { /*long running tasks code is written here... I have made the thread to sleep for 4 seconds*/ Thread.sleep(4000); Message msg = new Message(); Bundle bundle = new Bundle(); bundle.putString("Message", "Data transfered"); msg.setData(bundle); // send message to the handler handler.sendMessage(msg); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }).start(); }
The handler that processes the data sent by the worker thread is as below,
Handler handler = new Handler() { @Override public void handleMessage(Message msg) { // get the bundle and extract data by key Bundle bundle = msg.getData(); String message = bundle.getString("Message"); txt.setText(message); } };
So far we have learnt how to communicate with the handler using messages. Lets us now look at how to communicate with the handler using Runnable Object.
Communicating with the handler using Runnable Object
You can use handlers by passing Runnable object to the handler.post() method. Use the below code snippet to use runnables,
@Override public void onClick(View v) { // TODO Auto-generated method stub final Runnable r=null; //starting a new thread on click of a button new Thread(new Runnable() { public void run() { try { /*long running tasks code is written here... I have made the thread to sleep for 4 seconds*/ Thread.sleep(4000); handler.post(r); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }).start(); }
When you create any worker threads for your application to perform any time consuming tasks, it is recommended to keep the user informed saying that they will have to wait for sometime to get their work done. This is done by showing a progress dialog or a progress bar to the user. Please refer to the post ProgressDialog to know how to use threads and handlers in an application and how to keep the user informed about the background task. By using worker threads you can run time consuming tasks without blocking the User interface, however the long running tasks can be handled easily using AsyncTask. We would be discussing about AsyncTask in our next tutorial AsyncTask. Hope this tutorial helped you to understand the usage of Threads and handlers. Feel free to post your queries regarding this tutorial in comments section.