Saturday 21 January 2012

Send User Defined Class using AIDL RPC/IPC call (Parcelable)


      In my previous post we discuss about the AIDL (IPC method for Remote Service). But using AIDL method we can only pass primitive data type (int ,float, boolean, char etc. ) and some predefined classes like String, CharSequence, List, Map But when we want to pass the User defined class then AIDL unable to pass these classes without make it Parcelable.

Note : Why we are use Parcelable interface ? Ans   : In Android when we use AIDL for IPC then It use marshalling (Serialization) during sending the function argument, and Unmarshalling during receiving the same argument. But when we want to send and user defined object then it android unable to Marshall/Unmarshar , That why we need Parcelable. We make the object as Parcelable. It is used to transmit the message. It is a high-performance container for Android IPC.

The below example is modified of the my previous Post  (AIDL). So I just give necessary extended code used for Parcelable . You can get AIDL full code from here. In this example we pass a User defined class Student (data member name, father_name ) from client to service using setName() . In service side we capitalize the student name and again send Student object using getName() and show in Toast.

Steps to Implement the Parcelable over AIDL: Here I am giving some steps to implement the Parcelable.

Step1 : Create a different  .aidl file that will be used for define the Student class (Parcelable class).
(Student.aidl)
           package com.aidl; 
           parcelable Student;

we write this because aidl can detect Student class.

Step 2: Define the class as Parcelable by Implement the Parcelable.
(Student.java)

package com.aidl;
import android.os.Parcel;
import android.os.Parcelable;
public class Student implements Parcelable {
                public String name;
                public String father_name;
                public Student(Parcel source)
                {
                                name = source.readString();
                                father_name = source.readString();
                }
                public Student()
                {}
                public void setName(String name)
                {
                                this.name = name;
                }
                public void setFatherName(String father_name)
                {
                                this.father_name = father_name;
                }
                @Override
                public int describeContents() {
                                // TODO Auto-generated method stub
                                return 0;
                }
                @Override
                public void writeToParcel(Parcel dest, int flags) {
                                // TODO Auto-generated method stub
                                dest.writeString(name);
                                dest.writeString(father_name);
                               
                }
public static final Parcelable.Creator<Student> CREATOR = new Parcelable.Creator<Student>() {
                                @Override
                                public Student createFromParcel(Parcel source) {
                                                // TODO Auto-generated method stub
                                                return new Student(source);
                                }
                                @Override
                                public Student[] newArray(int size) {
                                                // TODO Auto-generated method stub
                                                return new Student[size];
                                }
                };
               
}

Explanation:
Yellow Color code : Parcelable interface has two abstract methods that we need to implement in class Student
  1.   describeContent()  : It is used for provide additional hint , on how the Parcelable will be received.
  2.  writeToParcel(Parcel dest, int flag) : This is the main method where we add data member to parcel using writeInt(), writeStirng () etc methods.
 "Green color code":  In any class which is implementing Parcelable have to provide CREATOR field. The type of CREATOR must be Parcelable.Creator<T>. Here in place of T we write the name of our class eg. Student. CREATOR is used during UnMarshalling of the object.

It has two methods –
1-      T createFromParcel(Parcel parcel) :This method is called when UnMarshalling happen during receiving the data. Keep care that  we receive the data member in same sequence as we write in writeToPacel(). Here we create a  constructor in which we demarshalling the data.
2-      NewArray(int size) : Here we just create an array of given size and return.
               
 For set the data in class I write setter methods setName() and setFatherName(). Its optional.

(Folder Structure)

Note : Better to keep this Student.java and Student.aidl in same package. And both should be copied in service side as well as client side.

Step 3: In the IRemoteInterface.aidl we change the argument type of methods

Student getName();
            void setName(in Student st);

When we are passing any User defined object in AIDL then we need to specify in/out with argument
        in – when the object is an IN Parameter
        out – when the object is an OUT Parameter
Change should be in both side client as well as service.

Step 4: client side, we create the T (Student) type object , set  the name and father name. And send using setName(Student ) method.

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
                  // TODO Auto-generated method stub
      Log.i("RemoteClient","In onServicen binded...");
      try {
            serviceInterface = IRemoteInterface.Stub.asInterface(service);
            Student st1 = new Student();
            st1.setName("Rahul");
            st1.setFatherName("Nand Kishor Kushwaha");
            serviceInterface.setName(st1);
      Log.i("RemoteClient","User Name = "+serviceInterface.getName().name);
      Toast.makeText(localContext, ""+serviceInterface.getName().name, Toast.LENGTH_SHORT).show();
            } catch (RemoteException e) {
            // TODO Auto-generated catch block
                  e.printStackTrace();
            }
      }
};

Step 5: Client side, In the next line I am getting the class Student from service using getName().

Log.i("RemoteClient","User Name = "+serviceInterface.getName().name);


Step 6: : Service Side, Here simply we receive the Student object in setName(). In getName() I alter the name field of object and return it.

private IRemoteInterface.Stub mBinder = new IRemoteInterface.Stub() {
            @Override
            public  Student getName() throws RemoteException {
                  // TODO Auto-generated method stub
                  stuInfo.name = stuInfo.name.toUpperCase();
                  return stuInfo;
            }
            @Override
            public void setName(Student st) throws RemoteException {
                  // TODO Auto-generated method stub
                  stuInfo = st;
                 
            }
      };




Thus we pass the user defined Object (Student object) from client to server and vice versa. 

NOTE : AIDL also not able to send Date type object. In the next Post I will show How to send Date Type object.







Monday 16 January 2012

AIDL (Remote Service Binding )


           If you want to access the service of different process /Application from your application then you need some IPC method because, In Android normally processes do not access other process memory.  Eg. User needs to user IPC method to access the Telephony Service. 


There are 2 method of IPC for Remote Service-
  1.        Messaging: - We will discuss this method in later posts.
  2.       AIDL (Android Interface Definition Language):-
In AIDL we create the .aidl file. This .aidl file contain an Interface containing methods those we want to access from our client application.  

Steps to use AIDL :-
  1. On Service side Create file with extension .aidl. In this file write an interface. Generally we keep this AIDL file in different package than the source file. Because the same AIDL file is needed in client side application. 
package com.aidl;

interface IRemoteInterface
{
      String getName();
      void setName(String a);
}

If we create the aidl in right way then eclipse automatically creates a java file in Gen folder.  This java file works as Interface between client and server.

2- Create the Service class

 package com.self; 
import com.aidl.IRemoteInterface; 
import android.app.Activity;
import android.app.Service;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class RemoteServer extends Service
{
      String userName;
      public void onCreate()
      {
            Log.i("RemoteServer","In onCreate");
      }
     
      public int onStartCommand(Intent intent, int flag, int statusID)
      {
            Log.i("RemoteServer","In onStartCommand");
            return START_STICKY;
      }
      @Override
      public IBinder onBind(Intent arg0) {
            // TODO Auto-generated method stub
           
            Log.i("RemoteServer","In onBind");
            return mBinder;
      }
     
      private IRemoteInterface.Stub mBinder = new IRemoteInterface.Stub() {
         @Override
            public String getName() throws RemoteException {
                  // TODO Auto-generated method stub
                  return userName.toUpperCase();
            }
             @Override
            public void setName(String a) throws RemoteException {
                  // TODO Auto-generated method stub
                  userName = a;
                 
            }
      };
}

Highlighted code shows the definition of AIDL interface methods. Here we set the String using setName() and get the same String in capital using getName(). 
As we can see when the client try to bind with service then onBind() returns the object of IRemoteInterface.stub object. This object is accessed in client side and using this mBinder object client access these two methods. 


3- In AndroidManifest.xml



    <service android:name = ".RemoteServer"
    android:process = ".remote">
    <intent-filter>
    <action android:name = "com.self.RemoteServer"></action>
    </intent-filter>
    </service>


4-  We copy same aidl file in client side in same package where it is created in Service side. Eg  com.aidl.


Folder Structure




5- Now write the Client side code. Where we start and bind with the service. Here we will create an Activity and call the startService() and bindService()

package com.lge;
import com.aidl.IRemoteInterface;
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.Toast;

public class RemoteClient extends Activity {
    /** Called when the activity is first created. */
      Button start;
      IRemoteInterface serviceInterface;
      Context localContext;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        start = (Button)findViewById(R.id.startServer);
        localContext = this;
        start.setOnClickListener(new OnClickListener() {
                 
                  @Override
                  public void onClick(View v) {
                        // TODO Auto-generated method stub
                        startServ();
                        bindServ();
                  }
            });
    }
   
   private ServiceConnection conn = new ServiceConnection() {
           
            @Override
            public void onServiceDisconnected(ComponentName name) {
                  // TODO Auto-generated method stub
                 
            }
           
            @Override
public void onServiceConnected(ComponentName name, IBinder service) {
                  // TODO Auto-generated method stub
Log.i("RemoteClient","In onServicen binded...");
      try {

      serviceInterface = IRemoteInterface.Stub.asInterface(service);

      serviceInterface.setName("Zest of Android");
      Log.i("RemoteClient","User Name =”+serviceInterface.getName());
      Toast.makeText(localContext, ""+serviceInterface.getName(), Toast.LENGTH_SHORT).show();
      } catch (RemoteException e) {
                        // TODO Auto-generated catch block
            e.printStackTrace();
            }
      }
};
   
      private void bindServ() {
            // TODO Auto-generated method stub
            startService(new Intent("com.self.RemoteServer"));
      }

      private void startServ() {
            // TODO Auto-generated method stub
            bindService(new Intent("com.self.RemoteServer") , conn, Context.BIND_AUTO_CREATE);
      }

}

6-  When we bind the service the in onServiceConnected() we convert to reference of AIDL. And using this object we can call the Remote service methods.

This is how we bind our Application with remote Service.