Android | Bind Service Using A Messenger - Truiton
Skip to content

Android | Bind Service Using A Messenger

Photo Credit: <a href="https://www.flickr.com/photos/59984780@N00/3352648778/">Chris DeCaro</a> via <a href="http://compfight.com">Compfight</a> <a href="https://www.flickr.com/help/general/#147">cc</a>

In Android, services are used for long running operations. Service is an android component without a UI and runs on the main thread of the app itself. But at times there are situations where we deliberately spawn a separate process for the service, as it gives better performance, abstraction etc. This approach also has some drawbacks like, if we start a service in a separate process, then one can not directly communicate with this service with a simple IBinder interface like in one of my previous examples. To do so we need to perform android interprocess communication by binding the service using a messenger.

Android Interprocess Communication :

In Android, interprocess communication can be done in two ways:

  1. Android Interface Definition Language (AIDL)
  2. Binding Service by Messenger

This Android Bind Service Using A Messenger tutorial focuses on the second way. Here we will bind a service and use an Android Messenger class in it for communication. In this android bound service example we would create a timer service, which we would later bind to an activity. This activity would send an Android Handler Message with a replyTo parameter filled, to the timer service, so that this message can be replied to with the current timestamp. But for the sake of completeness lets have a difference between the both IPC methodologies first:

Android AIDL vs Messenger :

As I mentioned above there are two ways through which we can perform inter-process communication in Android. Although both the approaches are sort of similar to each other functionally. The only difference is that when using an android service with messenger, all calls to the service are queued for execution and only one call at a time is executed. This gives us a flexibility, that we don’t need to write thread safe code. As binding an android service with messenger would automatically execute a single call at a time. Although its always a good programming practice to write thread safe code.

On the other hand if we use AIDL for inter-process communication, all calls to the service will be executed simultaneously. Hence we have to write thread safe code. AIDL is mostly used in cases where the service is contained in a separate application. As after writing an AIDL interface we also need to distribute the .aidl file which will be added in your current project’s directory structure. For detailed approach have a look at my tutorial on AIDL.

Hence if you are writing a service which has to be used in your app itself then I would advice to go for the Android messenger approach, otherwise go for AIDL. Unless you plan on executing calls to the service simultaneously.

Android Service with Messenger Example :

To start off lets have a small introduction to the working of an Android bound service where a messenger class is used. Messenger is a reference to the handler. In this implementation, messenger is used for communication between processes (IPC). When a service is bound to an activity, an IBinder object is returned in the activity. Through this IBinder object we can create a messenger object, which is used for sending the messages to the target handler. In this case, the target handler would be the service handler. When service would receive these messages in its handler implementation, it would reply accordingly. Lets have a look at the app manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.truiton.boundservice"
          xmlns:android="http://schemas.android.com/apk/res/android"
          android:versionCode="1"
          android:versionName="1.0">

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <service
            android:name="com.truiton.boundservice.BoundService"
            android:process=":truiton_process">
        </service>
    </application>

</manifest>

In the above manifest you can see that android:process attribute of service tag is used to start the service in a separate process. Also have a look at the image below:

Android Service Messenger Tutorial Processes

Next lets define a layout for the activity:

<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:background="#FFFFFF"
    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="com.truiton.boundservice.MainActivity" >

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="-80dp"
        android:src="@drawable/truiton_sq" />

    <Button
        android:id="@+id/print_timestamp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="130dp"
        android:text="Print Timestamp" />

    <TextView
        android:id="@+id/timestamp_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/print_timestamp"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="120dp"
        android:text=""
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <Button
        android:id="@+id/stop_service"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/print_timestamp"
        android:layout_centerHorizontal="true"
        android:text="Stop Service" />

</RelativeLayout>

This layout would render out to be something like this:

Android Service Messenger Tutorial

In this Android Service Messenger Tutorial, lets define the activity where this layout would be inflated and an Android service would be bound with messenger:

package com.truiton.boundservice;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

import java.lang.ref.WeakReference;

public class MainActivity extends AppCompatActivity {
    private Messenger mBoundServiceMessenger;
    private boolean mServiceConnected = false;
    private TextView mTimestampText;
    private final Messenger mActivityMessenger = new Messenger(
            new ActivityHandler(this));

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTimestampText = (TextView) findViewById(R.id.timestamp_text);
        Button printTimestampButton = (Button) findViewById(R.id.print_timestamp);
        Button stopServiceButon = (Button) findViewById(R.id.stop_service);
        printTimestampButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mServiceConnected) {
                    try {
                        Message msg = Message.obtain(null,
                                BoundService.MSG_GET_TIMESTAMP, 0, 0);
                        msg.replyTo = mActivityMessenger;
                        mBoundServiceMessenger.send(msg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        stopServiceButon.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mServiceConnected) {
                    unbindService(mServiceConnection);
                    mServiceConnected = false;
                }
                Intent intent = new Intent(MainActivity.this,
                        BoundService.class);
                stopService(intent);
            }
        });

    }

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent(this, BoundService.class);
        startService(intent);
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mServiceConnected) {
            unbindService(mServiceConnection);
            mServiceConnected = false;
        }
    }

    private ServiceConnection mServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mBoundServiceMessenger = null;
            mServiceConnected = false;
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBoundServiceMessenger = new Messenger(service);
            mServiceConnected = true;
        }
    };

    static class ActivityHandler extends Handler {
        private final WeakReference<MainActivity> mActivity;

        public ActivityHandler(MainActivity activity) {
            mActivity = new WeakReference<MainActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case BoundService.MSG_GET_TIMESTAMP: {
                    mActivity.get().mTimestampText.setText(msg.getData().getString(
                            "timestamp"));
                }
            }
        }

    }
}

All the magic of a bound service is initiated and ended in this activity. In this activity a service is first started and then bound to this activity itself. This prevents the android system from destroying this bound service in case the activity goes in background or gets destroyed. Further you may observe that an object of messenger is instantiated when this service is bound i.e when onServiceConnected method is called.

Another important thing to note here is that, this class has a messenger of its own by the name of mActivityMessenger. This activity messenger is instantiated through the handler object of this class, i.e ActivityHandler. This handler is used to receive the messages which are returned or replied to, from the service bound to this activity.

In this Android Service Messenger Tutorial, one of the key points to note is that, when an Android message is created, a replyTo parameter is used. This parameter specifies the messenger to which the reply message is to be sent. This parameter simplifies the process. Hence when this newly created message in the above code is received in the service, it is replied to this same activity’s messenger.

Further lets have a look at the bound service code:

package com.truiton.boundservice;

import java.lang.ref.WeakReference;

import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.widget.Chronometer;

public class BoundService extends Service {
 private static String LOG_TAG = "BoundService";
 private Chronometer mChronometer;
 static final int MSG_GET_TIMESTAMP = 1000;

 static class BoundServiceHandler extends Handler {
 private final WeakReference<BoundService> mService;

 public BoundServiceHandler(BoundService service) {
 mService = new WeakReference<BoundService>(service);
 }

 @Override
 public void handleMessage(Message msg) {
 switch (msg.what) {
 case MSG_GET_TIMESTAMP:
 long elapsedMillis = SystemClock.elapsedRealtime()
 - mService.get().mChronometer.getBase();
 int hours = (int) (elapsedMillis / 3600000);
 int minutes = (int) (elapsedMillis - hours * 3600000) / 60000;
 int seconds = (int) (elapsedMillis - hours * 3600000 - minutes * 60000) / 1000;
 int millis = (int) (elapsedMillis - hours * 3600000 - minutes
 * 60000 - seconds * 1000);
 Messenger activityMessenger = msg.replyTo;
 Bundle b = new Bundle();
 b.putString("timestamp", hours + ":" + minutes + ":" + seconds
 + ":" + millis);
 Message replyMsg = Message.obtain(null, MSG_GET_TIMESTAMP);
 replyMsg.setData(b);
 try {
 activityMessenger.send(replyMsg);
 } catch (RemoteException e) {
 e.printStackTrace();
 }
 break;
 default:
 super.handleMessage(msg);
 }
 }
 }

 final Messenger mMessenger = new Messenger(new BoundServiceHandler(this));

 @Override
 public void onCreate() {
 super.onCreate();
 Log.v(LOG_TAG, "in onCreate");
 mChronometer = new Chronometer(this);
 mChronometer.setBase(SystemClock.elapsedRealtime());
 mChronometer.start();
 }

 @Override
 public IBinder onBind(Intent intent) {
 Log.v(LOG_TAG, "in onBind");
 return mMessenger.getBinder();
 }

 @Override
 public void onRebind(Intent intent) {
 Log.v(LOG_TAG, "in onRebind");
 super.onRebind(intent);
 }

 @Override
 public boolean onUnbind(Intent intent) {
 Log.v(LOG_TAG, "in onUnbind");
 return true;
 }

 @Override
 public void onDestroy() {
 super.onDestroy();
 Log.v(LOG_TAG, "in onDestroy");
 mChronometer.stop();
 }
}

The above code sample completes the Android Bound Service with Messenger Tutorial. In the above bound service with messenger sample we are basically starting a timer by using the Android Chronometer class. This timer is started in the onCreate method of this service. For better understanding, please have a look at the full source code below:

Full Source Code

You may observe that this android bound service has a messenger of its own, mMessenger. This messenger is instantiated through an object of handler of this service i.e BoundServiceHandler. Basically this bound service handler is the handler where all the received messages would be handled or replied to.

Another important point to be noted in this Android Service Messenger Tutorial is that, all the handlers are static and use a Weak Reference of the service/activity. As if we don’t do this it would give a lint warning stating “This handler class should be static or leaks might occur”. By giving a weak reference in the handler we allow this service/activity to be garbage collected. Just a way of doing things right. Hope this helps.

Leave a Reply

Your email address will not be published. Required fields are marked *