Monday, 9 June 2014

Handler with Message and Background Thread


Handler

Handler provides a mechanism for communication between two threads running inside the application process. Android launcher activity runs inside the main thread of the application process. Sometimes we need to create another thread for background activities. Results generated by the another thread need to be dispatched to the UI thread. This is required when we try to update the UI elements like ImageView or Button view. Suppose you have downloaded an image from some web server and you want to put it to some ImageView inside your activity. To download the image, you must create a separate thread because UI thread does not allow to perform any network activity on UI thread. So you create another thread and downloaded the image, but that thread can’t update your ImageView directly. Here you need a Handler instance associated with the main UI thread. You need to create an instance of the Handler to use it to post a Runnable to the handler from the non UI thread. The Handler put this Runnable to the message queue of the looper of the UI thread. This runnable will be executed from that message queue, which will update the ImageView.

When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

According to the Handler official documentation there are mainly two main uses of the Handler class.

  1. Schedule message and runnable to be executed in future
  2. To enqueue an action to be performed on a different thread (Other than UI)

Let us understand the meaning of the first use. According to this sentence, you can do some work sometime in future. This work you can defined inside the run method method of a Runnable implementation. Then this unit of work will be posted to the handler instance using different variant of the post methods defined inside the Handler class. This post method will enqueue this unit of work inside the MessageQueue of the corresponding thread most of the case the UI thread. The looper which belongs to the thread will then dequeue the runnable and execute this. Like wise this posting of runnable, you can send the message using the handler to the message queue of the thread. To handle the method you need to create a custom Handler class and override the handleMessage Method.

Now, let us jump to the second use case of the Handler. This is very important in multi threading scenario. This will allow other thread to enqueue any action to be performed on some helper thread. One of such requirement  is modification of UI View from the helper thread. Once you have downloaded the image data from a network server, you simply pass that image data to the handler. Then handler will update the required view inside its handleMessage method. This is essential because the helper thread can not directly modify the UI view and you can not do the network operation over the UI thread.

Handler class provides a handful of methods to post Runnable and send messages. These methods are listed in below table. You can send messages immediately or at fixed time in future or delayed by a defined time. These overloaded methods are available in the Handler class.

Post runnable
Send Message
post(Runnable)
sendMessage(Message)
postAtTime(Runnable, long)
sendEmptyMessage(int)
postDelayed(Runnable, long)
sendMessageAtTime(Message, long)

sendMessageDelayed(Message, long)



In the above figure, UI thread has a looper which manages a MessageQueue. The Handlers in red color blocks are created inside the UI thread and are associated with the main thread only. There are two more custom threads are shown above. These threads have a reference of the handler. First thread post a unit of work i.e a runnable to the handler and handler what it does is simply put the runnable inside the MessageQueue. The second thread uses its own reference of the UI handler and send a message to this handler and the handler put this message to the MessageQueue.

These message are removed from the queue one by one over the UI thread and are processed. Looper run a message loop for a thread.

How to use hadler ?

  1. Create instance of a Handler inside the thread
  2. Define a message or a Runnable (Unit of work)
  3. Use this reference inside other thread to post or send the message to the first thread

An Example to send a message using Handler

Here I am going to give an example of Handler with Message using a background Thread. The example display the japan time on click of the button. The time is provided by different thread than UI and handler set the time in UI using handleMessage(Message).

I will going to explain Handler to post a Runnable in the next post.

 

MainActivity.java

package pp.handlerhavingmessage;

import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.Locale;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends Activity {

       private Handler handler = new Handler() {
                     @Override
                     public void handleMessage(Message msg) {
                               Bundle bundle = msg.getData();
                               String string = bundle.getString("key");
                               TextView myTextView = (TextView)findViewById(R.id.textView1);
                               myTextView.setText(string);
                     }
                    };

      
       @Override
       protected void onCreate(Bundle savedInstanceState) {
                   super.onCreate(savedInstanceState);
                   setContentView(R.layout.activity_main);
       }
      
       public void buttonClick(View view){
                  
       Runnable runnable = new Runnable() {
       public void run() {                                                         
                   Message message = handler.obtainMessage();
                               Bundle bundle = new Bundle();
                               SimpleDateFormat dateformat =
                        new SimpleDateFormat("HH:mm:ss MM/dd/yyyy", Locale.JAPAN);
                               String dateString = dateformat.format(new Date(0));
                               bundle.putString("key", dateString);
                               message.setData(bundle);
            handler.sendMessage(message);
           }
       };
         Thread mythread = new Thread(runnable);
         mythread.start();
            
       }
}

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:paddingBottom="@dimen/activity_vertical_margin"
   android:paddingLeft="@dimen/activity_horizontal_margin"
   android:paddingRight="@dimen/activity_horizontal_margin"
   android:paddingTop="@dimen/activity_vertical_margin"
   tools:context=".ThreadExampleActivity"
   android:background="#66f666" >

   <TextView
       android:id="@+id/textView1"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignLeft="@+id/button1"
       android:layout_alignRight="@+id/button1"
       android:layout_marginTop="35dp"
       android:background="#333333"
       android:text="Japan Time and Date"
       android:textColor="#ff0000"
       android:textSize="20sp" />

   <Button
       android:id="@+id/button1"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_below="@+id/textView1"
       android:layout_centerHorizontal="true"
       android:layout_marginTop="62dp"
       android:onClick="buttonClick"
       android:text="Run Handler Thread"
       android:background="#000fff"
       android:textSize="30sp" />

</RelativeLayout>


                           





No comments:

Post a Comment