Implementing Remote Interface Using AIDL



Each application in Android runs in its own process. An application cannot directly access another application's memory space. This is called application sandboxing.

In order to allow cross-application communication, Android provides an implementation of interprocess communication (IPC) protocol. IPC protocols tend to get complicated because of all the marshaling/un marshaling of data that is necessary.

To help with this, Android provides Android Interface Definition Language, or AIDL. It is a lightweight implementation of IPC using a syntax that is very familiar to Java developers, and a tool that automates the stub creation.

In order to allow for one application to call into another, we typically have to:

1. Define the AIDL interface
2. Implement the Stub for the remote service
3. Expose the remote service to the local client



Defining the AIDL

The AIDL syntax is very similar to regular Java interface. You simply define the method signature. The datatypes supported by AIDL are somewhat different than regular Java interfaces. For one, all Java primitive datatypes are supported. So are String, List, Map, and CharSequence classes. Also, all other AIDL datatypes that you defined are supported. In addition to that, all Parcelable classes are supported, but this is not going to be covered in this example. I'm trying to keep this example somewhat simpler to start with.


/src/com.marakana/IAdditionService.aidl

Code:

package com.marakana;

// Declare the interface.
interface IAdditionService {
// You can pass values in, out, or inout.
// Primitive datatypes (such as int, boolean, etc.) can only be passed in.
int add(in int value1, in int value2);
}



Implementing the Remote Service

Once you create your AIDL file and put it in the right place, the Eclipse+AIDL tool will generate a file with the same name, but .java extension. So I now have /gen/com.protechtraining.com/IAdditionService.java file. This is an auto-generated file so you don't want to edit it. What is important is that it contains a Stub class that we'll want to implement for our remote service.

To implement our remote service, we'll return the IBinder from onBind() method in our service class, AdditionService. The IBinder represents the implementation of the remote service. To implement IBinder, we subclass IAddtionService.Stub class from the auto-generated Java code, and provide implementation for our AIDL-defined methods, in this case add().



/src/com.marakana/AdditionService.java

Code:

package com.marakana;

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

/**
* This class exposes the remote service to the client
*/
public class AdditionService extends Service {
private static final String TAG = "AdditionService";

@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate()");
}

@Override
public IBinder onBind(Intent intent) {

return new IAdditionService.Stub() {
/**
* Implementation of the add() method
*/
public int add(int value1, int value2) throws RemoteException {
Log.d(TAG, String.format("AdditionService.add(%d, %d)",value1, value2));
return value1 + value2;
}

};
}

@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy()");
}
}



Exposing the Service Locally

Once we have the service implement the onBind() properly, we are ready to connect to that service from our client. In this case, we have AIDLDemo activity connecting to that service. To establish the connection, we need to implement ServiceConnection class. Our activity in this example provides this implementation in AdditionServiceConnection inner class by implementing onServiceConnected() and onServiceDiconnected() methods. Those callbacks will get the stub implementation of the remote service upon connection. We need to cast them from stubs to our AIDL service implementation. To do that, we use the IAdditionService.Stub.asInterface((IBinder) boundService) helper method.

From that point on, we have a local service object that we can use to make calls against the remote service.


/src/com.marakana/AIDLDemo.java

Code:

package com.marakana;

import android.app.Activity;
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.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class AIDLDemo extends Activity {
private static final String TAG = "AIDLDemo";
IAdditionService service;
AdditionServiceConnection connection;

/**
* This class represents the actual service connection. It casts the bound
* stub implementation of the service to the AIDL interface.
*/
class AdditionServiceConnection implements ServiceConnection {

public void onServiceConnected(ComponentName name, IBinder boundService) {
service = IAdditionService.Stub.asInterface((IBinder) boundService);
Log.d(AIDLDemo.TAG, "onServiceConnected() connected");
Toast.makeText(AIDLDemo.this, "Service connected", Toast.LENGTH_LONG)
.show();
}

public void onServiceDisconnected(ComponentName name) {
service = null;
Log.d(AIDLDemo.TAG, "onServiceDisconnected() disconnected");
Toast.makeText(AIDLDemo.this, "Service connected", Toast.LENGTH_LONG)
.show();
}
}

/** Binds this activity to the service. */
private void initService() {
connection = new AdditionServiceConnection();
Intent i = new Intent();
i.setClassName("com.marakana", com.marakana.AdditionService.class.getName());
boolean ret = bindService(i, connection, Context.BIND_AUTO_CREATE);
Log.d(TAG, "initService() bound with " + ret);
}

/** Unbinds this activity from the service. */
private void releaseService() {
unbindService(connection);
connection = null;
Log.d(TAG, "releaseService() unbound.");
}

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

initService();

// Setup the UI
Button buttonCalc = (Button) findViewById(R.id.buttonCalc);

buttonCalc.setOnClickListener(new OnClickListener() {
TextView result = (TextView) findViewById(R.id.result);
EditText value1 = (EditText) findViewById(R.id.value1);
EditText value2 = (EditText) findViewById(R.id.value2);

public void onClick(View v) {
int v1, v2, res = -1;
v1 = Integer.parseInt(value1.getText().toString());
v2 = Integer.parseInt(value2.getText().toString());

try {
res = service.add(v1, v2);
} catch (RemoteException e) {
Log.d(AIDLDemo.TAG, "onClick failed with: " + e);
e.printStackTrace();
}
result.setText(new Integer(res).toString());
}
});
}

/** Called when the activity is about to be destroyed. */
@Override
protected void onDestroy() {
releaseService();
}

}

The UI in this case is very simple. There are couple of TextViews and EditText fields and a button The button handles its events in an anonymous inner class OnClickListener. The button simply invokes the add() method on the service as if it was a local call.


The layout for this example is not that significant, but I'm including it here for completeness purposes.


/res/layout/main.xml
Code:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content" android:text="AIDL Demo"
android:textSize="22sp" />
<EditText android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/value1"
android:hint="Value 1"></EditText>
<TextView android:id="@+id/TextView01" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="+"
android:textSize="36sp"></TextView>
<EditText android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/value2"
android:hint="Value 2"></EditText>
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/buttonCalc"
android:text="="></Button>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="result"
android:textSize="36sp" android:id="@+id/result"></TextView>
</LinearLayout>

The output of your code should look like this:


Output


Source code
https://www.protechtraining.com/static/tutorials/AIDLDemo.zip

Additional Resources
I highly recommend getting the details on AIDL from Designing a Remote Interface Using AIDL.

Published November 25, 2009