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.







2 comments: