Android之AIDL(进程间通信)

本文介绍了在Android中使用AIDL设计远程接口进行进程间通信的方法,包括创建AIDL接口文件、实现接口、向客户端公开接口以及调用接口中的方法。此外,还详细解释了如何实现对象的Parcelable接口以在不同进程间传递对象。

原文地址::http://blog.youkuaiyun.com/huangbiao86/article/details/7096368

 

 

前几天学习了Android里进程间的通信。

使用AIDL设计远程接口(Designing  a Remote  Interface  Using AIDL)

由于每个应用程序都运行在自己的进程空间,并且可以从应用程序UI运行另一个服务进程,而且经常会在不同的进程间传递对象。在Android平台,一个进程通常不能访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的基本单元,并且有序的通过进程边界。

通过代码来实现这个数据传输过程是冗长乏味的,Android提供了AIDL工具来处理这项工作。AIDL  (Android  Interface Definition  Language)是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(IPC)的代码。如果在一个进程中(例如Acti vity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。

AIDL  IPC机制是面向接口的,像COM或Corba一样,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。

使用AIDL实现IPC 服务的步骤是:

1、首先创建aidl文件,该文件(YourInterface.aidl)定义了客户端可用的方法和数据的接口。它的定义方法与接口是一样的;(会在gen目录下面自动生成对应的YourInterface .java文件)

IRemoteService.aidl

  1. package hb.android.aidl;  
  2.   
  3. interface IRemoteService {  
  4.     int getPid();  
  5.   
  6.     void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,  
  7.             double aDouble, String aString);  
  8. }  
IRemoteService2.aidl
  1. package hb.android.aidl;  
  2.   
  3. interface IRemoteService2{  
  4.     void sysout();  
  5.     String returnStr();  
  6. }  
自动生成的java文件如图:


2、  实现接口-AIDL编译器从AIDL接口文件中利用Java语言创建接口,该接口有一个继承的命名为Stub的内部抽象类(并且实现了一些IPC 调用的附加方法),要做的就是创建一个继承于YourInterface.Stub 的类并且实现在.aidl 文件中声明的方法。(一般是在服务中实现这个接口,在服务中使用IPC)

  1. /** 
  2.      * 实现定义的aidl接口。 
  3.      */  
  4.     private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {  
  5.         public int getPid() {  
  6.             return Process.myPid();  
  7.         }  
  8.   
  9.         public void basicTypes(int anInt, long aLong, boolean aBoolean,  
  10.                 float aFloat, double aDouble, String aString) {  
  11.             // Does nothing   
  12.             System.out.println("basicTypes");  
  13.         }  
  14.     };  
  15.     /** 
  16.      * 实现定义的aidl接口。 
  17.      */  
  18.     private final IRemoteService2.Stub mBinder2 = new IRemoteService2.Stub() {  
  19.         @Override  
  20.         public void sysout() {  
  21.             System.out.println("This is IremoteService2.Stub.sysout()");  
  22.         }  
  23.   
  24.         @Override  
  25.         public String returnStr() throws RemoteException {  
  26.             return "This is IRemoteServicfe2.Stub.returnStr()";  
  27.         }  
  28.     };  

3、向客户端公开(暴露)接口-如果是编写服务,应该继承Service并且重载Service.onBind(Intent) 以返回实现了接口的对象实例

  1. /** 
  2.      * 返回IBinder,在这里可以根据选择的接口进行返回哪一个 如果只有一个接口就不需要判断,直接返回就可以了。 
  3.      */  
  4.     @Override  
  5.     public IBinder onBind(Intent intent) {  
  6.         if (IRemoteService2.class.getName().equals(intent.getAction())) {  
  7.             return mBinder2;  
  8.         }  
  9.         if (IRemoteService.class.getName().equals(intent.getAction())) {  
  10.             return mBinder;  
  11.         }  
  12.         return null;  
  13.     }  

4、 运行这个程序,那么服务就已经安装到系统了。接下来就是客户端访问服务,利用服务,进行进程间的通信。

下面再看看如何调用IPC 方法(Calling  an  IPC Method)

1、   声明.aidl 文件中定义的接口类型的变量和方法。

IRemoteService.aidl

  1. package hb.android.aidl;  
  2.   
  3. interface IRemoteService {  
  4.     int getPid();  
  5.   
  6.     void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,  
  7.             double aDouble, String aString);  
  8. }  
IRemoteService2.aidl
  1. package hb.android.aidl;  
  2.   
  3. interface IRemoteService2 {  
  4.     void sysout();  
  5.     String returnStr();  
  6. }  

2、   实现ServiceConnection

  1. /** 
  2.      * ServiceConnection与Service想链接,IRemoteService.Stub.asInterface(service); 
  3.      * 会返回与当前的aidl文件相关的类的实例,这样就可以获取到远程的远程服务实现的IRemoteService接口了。 
  4.      * 注意如果一个ServiceConnection同一时刻只能与Service连接 
  5.      * 。如果需要同时连接一个服务里的多个aidl接口,那么必须定义两个ServiceConnection获取 
  6.      * ,因为一个ServiceConnection连接会返回一个aidl接口的实例。 
  7.      */  
  8.     private ServiceConnection mConnection = new ServiceConnection() {  
  9.         // Called when the connection with the service is established   
  10.         public void onServiceConnected(ComponentName className, IBinder service) {  
  11.             // Following the example above for an AIDL interface,   
  12.             // this gets an instance of the IRemoteInterface, which we can use   
  13.             // to call on the service   
  14.             try {  
  15.                 if (service.getInterfaceDescriptor().equals(  
  16.                         IRemoteService.class.getName())) {  
  17.                     mIRemoteService = IRemoteService.Stub.asInterface(service);  
  18.                     mIRemoteService.basicTypes(00false00null);  
  19.                 }  
  20.             } catch (RemoteException e) {  
  21.                 e.printStackTrace();  
  22.             }  
  23.         }  
  24.   
  25.         // Called when the connection with the service disconnects unexpectedly   
  26.         public void onServiceDisconnected(ComponentName className) {  
  27.             System.out.println("Service has unexpectedly disconnected");  
  28.             mIRemoteService = null;  
  29.         }  
  30.     };  
  31.     private ServiceConnection mConnection2 = new ServiceConnection() {  
  32.   
  33.         @Override  
  34.         public void onServiceDisconnected(ComponentName name) {  
  35.             System.out.println("Service2 has unexpectedly disconnected");  
  36.             mIRemoteService2 = null;  
  37.         }  
  38.   
  39.         @Override  
  40.         public void onServiceConnected(ComponentName name, IBinder service) {  
  41.             try {  
  42.                 if (service.getInterfaceDescriptor().equals(  
  43.                         IRemoteService2.class.getName())) {  
  44.                     mIRemoteService2 = IRemoteService2.Stub  
  45.                             .asInterface(service);  
  46.                     mIRemoteService2.sysout();  
  47.                 }  
  48.             } catch (RemoteException e) {  
  49.                 e.printStackTrace();  
  50.             }  
  51.         }  
  52.     };  

3、   调用Context.bindService(),传递ServiceConnection的实现

  1. intent = new Intent(IRemoteService.class.getName());  
  2.         System.out.println(IRemoteService.class.getName());  
  3.         bindService(intent, mConnection, Context.BIND_AUTO_CREATE);  
  4.   
  5.         intent2 = new Intent(IRemoteService2.class.getName());  
  6.         System.out.println(IRemoteService2.class.getName());  
  7.         bindService(intent2, mConnection2, BIND_AUTO_CREATE);  

4、   在ServiceConnection.onServiceConnected()方法中会接收到IBinder对象,调用YourInterfaceName.Stub.asInterface((IBinder)service)将返回值转换为YourInterface类型

  1. mIRemoteService = IRemoteService.Stub.asInterface(service);  
  2.                 mIRemoteService.basicTypes(00false00null);  

5、   调用接口中定义的方法。应该总是捕获连接被打断时抛出的DeadObjectException异常,这是远端方法唯一的异常。


6、   调用Context.unbindService()断开连接;

  1. protected void onDestroy() {  
  2.         super.onDestroy();  
  3.         unbindService(mConnection);  
  4.         unbindService(mConnection2);  
  5.     };  

如果想利用进程间的通信来传递对象,那么该对象必须实现Parcelable,下面详细步骤:

1、 使该类实现Parcelabel接口。 

  1. package hb.android.entry;  
  2.   
  3. import android.os.Parcel;  
  4. import android.os.Parcelable;  
  5.   
  6. /** 
  7.  * 在进行间通信必须实现Parcelable接口 
  8.  *  
  9.  * @author Administrator 
  10.  *  
  11.  */  
  12. public class Person implements Parcelable {  
  13.   
  14.     public String name;  
  15.     public String sex;  
  16.     public String tel;  
  17.   
  18.     public Person() {  
  19.         super();  
  20.     }  
  21.   
  22.     public Person(Parcel in) {  
  23.         readFromParcel(in);  
  24.     }  
  25.   
  26.     public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {  
  27.   
  28.         /** 
  29.          * 实现从in中创建出类的实例的功能 
  30.          */  
  31.         public Person createFromParcel(Parcel in) {  
  32.             return new Person(in);  
  33.         }  
  34.   
  35.         /** 
  36.          * 创建一个类型为T,长度为size的数组,仅一句话(return new T[size])即可。估计本方法是供外部类反序列化本类数组使用。 
  37.          */  
  38.         public Person[] newArray(int size) {  
  39.             return new Person[size];  
  40.         }  
  41.     };  
  42.   
  43.     /** 
  44.      * 从一个外部的Parcel对象读取对象。顺序必须与writeToParcel对应,不然会出错。 
  45.      *  
  46.      * @param in 
  47.      */  
  48.     public void readFromParcel(Parcel in) {  
  49.         name = in.readString();  
  50.         sex = in.readString();  
  51.         tel = in.readString();  
  52.     }  
  53.   
  54.     /** 
  55.      * 该方法将类的数据写入外部提供的Parcel中 
  56.      */  
  57.     @Override  
  58.     public void writeToParcel(Parcel dest, int flags) {  
  59.         dest.writeString(name);  
  60.         dest.writeString(sex);  
  61.         dest.writeString(tel);  
  62.     }  
  63.   
  64.     /** 
  65.      * 没搞懂有什么用,反正直接返回0也可以 
  66.      */  
  67.     @Override  
  68.     public int describeContents() {  
  69.         return 0;  
  70.     }  
  71.   
  72.     public String getName() {  
  73.         return name;  
  74.     }  
  75.   
  76.     public void setName(String name) {  
  77.         this.name = name;  
  78.     }  
  79.   
  80.     public String getSex() {  
  81.         return sex;  
  82.     }  
  83.   
  84.     public void setSex(String sex) {  
  85.         this.sex = sex;  
  86.     }  
  87.   
  88.     public String getTel() {  
  89.         return tel;  
  90.     }  
  91.   
  92.     public void setTel(String tel) {  
  93.         this.tel = tel;  
  94.     }  
  95. }  

2、实现public  void  writeToParcel(Parcel  out) 方法,以便可以将对象的当前状态写入包装对象


3、增加名为CREATOR的构造器到类中,并实现Parcelable.Creator接口。

4、  最后,但同样重要的是,创建AIDL文件声明这个可打包的类(见下文),如果使用的是自定义的编译过程,那么不要编译此AIDL文件,它像C语言的头文件一样不需要编译。


AIDL会使用这些方法的成员序列化和反序列化对象。  

两个实例的源码下载:http://download.youkuaiyun.com/download/huangbiao86/3958487

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值