Android Service: Interprocess Communication With AIDL - Truiton
Skip to content

Android Service: Interprocess Communication With AIDL

Photo Credit: Niels Linneberg via Compfight cc

Android services are designed for many permutations and combinations. You can use them for long running operations, you can use them for long running network operations, you can use them for never ending operations in a separate process. One of these many permutations and combinations is Android service interprocess communication through the usage of AIDL. The Android Interface Definition Language. In this tutorial I would show you how to create an interface through AIDL and use it for interprocess communication between a service an activity.

Before starting with the tutorial let me put down some facts for beginners.

  1. Android services are not background tasks, they run on your app’s main thread only.
  2. An Android service can be started in a different process, which would free your app’s main thread.
  3. If an Android service is started in a different process, you cannot communicate with that service through a normal IBinder interface.

To do so we need to establish an inter process communication between the android service (in process A) and the activity (in process B). One of the two ways of establishing interprocess communication (IPC) is through, the use of AIDL. Although it sounds a little complex, but believe me its not that difficult. All you need to do is, create an interface, like a normal java interface. Then place it in your file structure with an extension .aidl. Just like the image shown below.

Android Bound Service AIDL

AIDL : Android Interface Definition Language

To perform IPC (Inter Process Communication) in Android, one needs to define a set of methods through which a process can be accessed from a remote process. Through AIDL we can define these methods. AIDL is just like any other interface in Java, where abstract methods are defined. To do so we need to create a file with .aidl extension and define all the abstract methods in it, which are to be accessed outside this process.

One of the main features of AIDL is that, by using this we can communicate between two applications. Although if you don’t need to communicate with a different app (in a different process of course), this approach should not be used. As this feature also imposes a direct restriction on the implementation. That only a primitive data type, and some other basic data types like, string, lists, maps, etc. can be used as parameters for methods. If you want to use a custom class object as a parameter, then you need to implement the Parcelable interface in that object’s class and import it in the AIDL too. This can be a separate tutorial in itself, therefore for the sake of simplicity I have not included it in this Android Service Interprocess Communication With AIDL tutorial.

While designing the Android service which will implement this AIDL interface, keep in mind that this service can be accessed by more than one application at a time. Therefore you need to implement this service as a multi-threaded service, assuming any method defined in this service can be executed simultaneously by more than one application.

Android Inter Process Communication In Same App

In this Android Service: IPC With AIDL tutorial, my aim is to get the running time of an Android service in an activity. To do this I will start an Android service in a process, with a timer running inside it. Then I will bind it to an activity in a separate process. I will be doing all of this, in the same app. As in normal scenarios, mostly when a service is started, it is accessed in the same app first. Although if it needs to be accessed in a different app, it will be accessed in the same way. Moving on lets have a look at the code:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.truiton.boundservice"
    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 highlighted, the service is started in a separate process. Reason being we have to perform Android Service Interprocess Communication With AIDL. Next lets define the Android AIDL file.

package com.truiton.boundservice.aidl;

interface IBoundService{
	int getPid();
	String getTimestamp();
}

After creating this file with .aidl extension, you need to put this file in the /src folder of your project. After this, Android SDK tools will automatically generate an IBinder interface in your /gen folder. You can access this interface through a Stub subclass, which will also be created automatically. Have a look at the service code below:

package com.truiton.boundservice;

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

import com.truiton.boundservice.aidl.IBoundService;

public class BoundService extends Service {
 private static String LOG_TAG = "BoundService";
 private Chronometer mChronometer;

 private IBoundService.Stub mBinder = new IBoundService.Stub() {
 @Override
 public int getPid() throws RemoteException {
 return this.getPid();
 }

 @Override
 public String getTimestamp() throws RemoteException {
 long elapsedMillis = SystemClock.elapsedRealtime()
 - 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);
 return hours + ":" + minutes + ":" + seconds + ":" + millis;
 }
 };

 @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 mBinder;
 }

 @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();
 }
}

As you can see the above implementation of Android service is very basic, its running a Chronometer to keep track of the service start time. In this Android service inter process communication with AIDL example my aim is to get the running time of this service in an activity. Now if you remember this service is running in a separate process, therefore to get the running time of this service out of this process, we need to perform inter process communication. Hence getTimestamp() method defined in the AIDL, is implemented here.

Android Bound Service AIDL

Next lets have a look at the layout file of the activity, where this method would be accessed:

<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>

After rendering this layout, it would look something like this:

Android Bound Service AIDL

This layout would be inflated in MainActivity.java, lets have a look at the code for this class:

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.IBinder;
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 com.truiton.boundservice.aidl.IBoundService;

public class MainActivity extends AppCompatActivity {
    IBoundService mBoundServiceInterface;
    boolean mServiceConnected = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TextView timestampText = (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 {
                        timestampText.setText((mBoundServiceInterface)
                                .getTimestamp());
                    } 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) {
            mServiceConnected = false;
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBoundServiceInterface = IBoundService.Stub.asInterface(service);
            mServiceConnected = true;
        }
    };
}

As you can see in the above class, an android service by the name of BoundService is started by calling startService() method. Further the same service is bound by calling the bindService() method. But here the binding process is a little different from standard Android bound service. Here if you see closely IBoundService.Stub.asInterface(service) is used to get the interface in this activity, through which the getTimestamp() method is called, which prints the time stamp. To get a better understanding, have a look at the full source code below:

Full Source Code

For the sake of simplicity in this Android Service Interprocess Communication With AIDL tutorial I have used the same app to access the service which was started in a different process. But if you are trying to access the service in a different app, don’t forget to add the .aidl file in that app’s /src folder structure. By doing this you would be able to access the remote service’s methods. Hope this helps.

2 thoughts on “Android Service: Interprocess Communication With AIDL”

Leave a Reply

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