Tuesday 2 June 2015

Android AIDL example and tutorial

http://www.compiletimeerror.com/2015/01/android-aidl-tutorial-with-example.html#.VW2xp1yqpBc


Introduction 
            Every Android application runs in its own process. So one application cannot access another application's memory space OR in other words, one process cannot access memory of other process. So to let this happen we need to decompose their objects into primitives that the operating system can understand and marshall the objects across that boundary for you.
 The code to do that marshalling is tedious to write, so Android handles it for you with AIDL - meaning, Inter Process communication (IPC) can be handled easily through AIDL. For better understanding, view this as a communication between client and server applications. Client sends a request and server responds back.
           This tutorial will explain this inter-process communication in simple manner with clear example and source code. To make our example simple, lets take calculation request. Client application will get two numbers from the user and sends to the server application. Server application will perform addition of these numbers and returns the sum to the client application.

AIDL can be achieved following the below steps :
Define AIDL Interface - AIDL that Interfaces your applications. 
Implement the Interface -  The above interface will be implemented in the service so that clients can access.
Expose the Interface to clients- To expose the interface for your service, extend Service and implement onBind()

Let me give a high level picture on what this example will contain. We need to have two applications - client and server. Client App will get numbers from user, for which we need a UI and corresponding java code to fetch the numbers and send it to the server then get the result and display back in he UI.
Server app is going to be a service that will receive numbers from the client, perform addition and send the result to client. Thats it..

Server Application (Android_AdditionService) 
Lets see how to code server application for AIDL with example


Server App Project Directory

Define AIDL Interface
                 This file defines programming interface with method signature. AIDL interface should be defined in a .aidl file. In our case its IAdd.aidl with the below code.

IAdd.aidl
package com.example.android_additionservice;
interface IAdd
{
  int add(int num1, int num2);

}
When we build our application, Android SDK generates .java file corresponding to this .aidl file with the same name like IAdd.java. The generated interface includes a subclass named Stub that is an abstract implementation of its parent interface and declares all the methods from the .aidl file

Implement the Interface (AdditionService.java)
                 To implement the interface generated from the .aidl, extend the generated Binder interface and implement the methods inherited from the .aidl file. On our case we need to implement the add method here. Now the mBinder is an instance of the Stub class (a Binder), which defines the RPC interface for the service.
private final IAdd.Stub mBinder = new IAdd.Stub() {
@Override
public int add(int num1, int num2) throws RemoteException {
// TODO Auto-generated method stub
return (num1 + num2);
}
};

Expose Interface to Clients (AdditionService.java)
                 After implementing the interface, we need to expose it to other applications to access it. For which extend service and implement onBind() to return an instance of your class that implements the generated stub.

public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return mBinder;
}

AdditionService.java
package com.example.android_additionservice;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import com.example.android_additionservice.IAdd;

public class AdditionService extends Service {
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return mBinder;
}

/**
 * IAdd definition is below
 */
private final IAdd.Stub mBinder = new IAdd.Stub() {
@Override
public int add(int num1, int num2) throws RemoteException {
// TODO Auto-generated method stub
return (num1 + num2);
}
};
}

Register Service in AndroidManifest.xml
         Since the server application is a service, lets register this in the AndroidManifest.xml file.

AndroidManifest.xml

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

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.android_additionservice.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=".AdditionService"
            android:process=":remote" >
            <intent-filter>
                <action android:name="service.Calculator" />
            </intent-filter>
        </service>
    </application>

</manifest>


Thats it.. We are done with the server application. Now lets see how to code the client application

Client Application
Lets see how to code client application for AIDL with example
Client Project Directory
           
Create User Interface
            We need to  place EditText fields, button fields , TextView fields in activity_main.xml as appropriate for addition logic

Include IAdd.aidl file from the server application
             Place the same copy of the AIDL file in your client application also, with the similar package name.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/sum"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

    <EditText
        android:id="@+id/value1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

    <EditText
        android:id="@+id/value2"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:weightSum="3" >

        <Button
            android:id="@+id/add"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Add" />
    </LinearLayout>

</LinearLayout>

Client logic
             Ultimate aim here is to establish connection between your client application and server application. First get the user entered values in the UI and then initiate connection to the server. For this create a new inner class by implementing ServiceConnection class in our MainActivity class. 
Also we need to implement onServiceConnected() and onServiceDiconnected()  methods in this class.These methods are the callbacks, which will get the stub implementation of the remote service on connection.
             In your implementation of onServiceConnected(), you will receive an IBinder instance (called service). Call IAdd.Stub.asInterface((IBinder)service) to cast the returned parameter to our interface type. Then bind to the service as follows,

Intent it = new Intent();
it.setAction("service.Calculator");
bindService(it, AddServiceConnection, Service.BIND_AUTO_CREATE);

where service.Calculator  is the action name of the intent-filter in Server application. Check AndroidManifest,xml in Server application.

MainActivity.java

package com.example.androidaidl_addition;

import com.example.android_additionservice.IAdd;
import com.example.androidaidl_addition.R;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
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 MainActivity extends Activity implements OnClickListener {
EditText etValue1, etValue2;
Button bAdd;
TextView mSum;
protected IAdd AddService;
ServiceConnection AddServiceConnection;

/** Called when the activity is first created. */

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etValue1 = (EditText) findViewById(R.id.value1);
etValue2 = (EditText) findViewById(R.id.value2);
mSum = (TextView) findViewById(R.id.sum);
bAdd = (Button) findViewById(R.id.add);
bAdd.setOnClickListener(this);
initConnection();
}

void initConnection() {
AddServiceConnection = new ServiceConnection() {

@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
AddService = null;
Toast.makeText(getApplicationContext(), "Service Disconnected",
Toast.LENGTH_SHORT).show();
Log.d("IRemote", "Binding - Service disconnected");
}

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
AddService = IAdd.Stub.asInterface((IBinder) service);
Toast.makeText(getApplicationContext(),
"Addition Service Connected", Toast.LENGTH_SHORT)
.show();
Log.d("IRemote", "Binding is done - Service connected");
}
};
if (AddService == null) {
Intent it = new Intent();
it.setAction("service.Calculator");
// binding to remote service
bindService(it, AddServiceConnection, Service.BIND_AUTO_CREATE);
}
}

protected void onDestroy() {
super.onDestroy();
unbindService(AddServiceConnection);
};

public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.add: {
int num1 = Integer.parseInt(etValue1.getText().toString());
int num2 = Integer.parseInt(etValue2.getText().toString());
try {
mSum.setText("Result:" + AddService.add(num1, num2));
Log.d("IRemote", "Binding - Add operation");
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
break;
}
}
}

Steps to Run the applications
1. First Run the Service application, in our case its Android_AdditionService. This will display Hello     World.
2. Run the client application, which is AndroidAIDL_Addition. Input values and check the result.

Installed Apps

Server App Connected

Result

Thats it.., Hope this Android AIDL example, give you a better overview about communication between two applications.

Possible Exception in AIDL programming
Exception name  - SecurityException: Binder invocation to an incorrect interface
Solution  - We may encounter this exception very often in AIDL programming, the reason for this exception is, the AIDL interface(IAdd.aidl) created in the server should be place in the client also. The package name in the client should be the exact replica as in server, if not Android will throw this exception. In our case we have place AIDL files in client and the server under com.example.android_additionservice package.

1 comment:

  1. set package to Intent to start

    it.setPackage("com.example.android_additionservice");

    ReplyDelete