Android IPC 系列(2.3):AIDL

Aidl默认支持的类型包话java基本类型(int、long、boolean等)和(String、List、Map、CharSequence),如果要传递自定义的类型该如何实现呢?
要传递自定义类型,首先要让自定义类型支持parcelable协议,实现步骤如下:
1>自定义类型必须实现Parcelable接口,并且实现Parcelable接口的public void writeToParcel(Parcel dest, int flags)方法 。
2>自定义类型中必须含有一个名称为CREATOR的静态成员,该成员对象要求实现Parcelable.Creator接口及其方法。
3> 创建一个aidl文件声明你的自定义类型。
Parcelable接口的作用:实现了Parcelable接口的实例可以将自身的状态信息(状态信息通常指的是各成员变量的值)写入Parcel,也可以从Parcel中恢复其状态。 Parcel用来完成数据的序列化传递。

 
1> 创建自定义类型,并实现Parcelable接口,使其支持parcelable协议。如:在cn.jp.domain包下创建Person.java:

[java] view plain copy print ?
  1. package cn.jp.domain;  
  2. import android.os.Parcel;  
  3. import android.os.Parcelable;  
  4. public class Personimplements Parcelable  
  5. private Integer id;  
  6. private String name;  
  7. public Person(){}  
  8. public Person(Integer id, String name) {  
  9. this.id = id;  
  10. this.name = name;  
  11. }  
  12. public Integer getId() {  
  13. return id;  
  14. }  
  15. public void setId(Integer id) {  
  16. this.id = id;  
  17. }  
  18. public String getName() {  
  19. return name;  
  20. }  
  21. public void setName(String name) {  
  22. this.name = name;  
  23. }  
  24. @Override  
  25. public int describeContents() {  
  26. return 0;  
  27. }  
  28. @Override  
  29. public void writeToParcel(Parcel dest,int flags) {//把javanbean中的数据写到Parcel 
  30. dest.writeInt(this.id);  
  31. dest.writeString(this.name);  
  32. }  
  33. //添加一个静态成员,名为CREATOR,该对象实现了Parcelable.Creator接口 
  34. public staticfinal Parcelable.Creator<Person> CREATOR =new Parcelable.Creator<Person>(){  
  35. @Override  
  36. public Person createFromParcel(Parcel source) {//从Parcel中读取数据,返回person对象 
  37. return new Person(source.readInt(), source.readString());  
  38. }  
  39. @Override  
  40. public Person[] newArray(int size) {  
  41. return new Person[size];  
  42. }  
  43. };  
  44. }  
2> 在自定义类型所在包下创建一个aidl文件对自定义类型进行声明,文件的名称与自定义类型同名。

[java] view plain copy print ?
  1. package cn.jp.domain;  
  2. parcelable Person;  

3> 在接口aidl文件中使用自定义类型,需要使用import显式导入,本例在cn.jp.aidl包下创建IPersonService.aidl文件,内容如下:

[java] view plain copy print ?
  1. package cn.itcast.aidl;  
  2. import cn.itcast.domain.Person;  
  3. interface IPersonService {  
  4.       void save(in Person person);  
  5. }  

4> 在实现aidl文件生成的接口(本例是IPersonService),但并非直接实现接口,而是通过继承接口的Stub来实现(Stub抽象类内部实现了aidl接口),并且实现接口方法的代码。内容如下:

[java] view plain copy print ?
  1. public class ServiceBinderextends IPersonService.Stub {  
  2.        @Override  
  3.        public void save(Person person)throws RemoteException {  
  4. Log.i("PersonService", person.getId()+"="+ person.getName());  
  5.        }  
  6. }  

5> 创建一个Service(服务),在服务的onBind(Intent intent)方法中返回实现了aidl接口的对象(本例是ServiceBinder)。内容如下:

[java] view plain copy print ?
  1. public class PersonServiceextends Service {  
  2. private ServiceBinder serviceBinder =new ServiceBinder();  
  3. @Override  
  4. public IBinder onBind(Intent intent) {  
  5. return serviceBinder;  
  6. }  
  7. public class ServiceBinderextends IPersonService.Stub {  
  8.        @Override  
  9.        public void save(Person person)throws RemoteException {  
  10. Log.i("PersonService", person.getId()+"="+ person.getName());  
  11.        }  
  12. }  
  13. }  

其他应用可以通过隐式意图访问服务,意图的动作可以自定义,AndroidManifest.xml配置代码如下:

[html] view plain copy print ?
  1. <serviceandroid:name=".PersonService">  
  2. <intent-filter>  
  3. <actionandroid:name="cn.jp.process.aidl.PersonService "/>  
  4. </intent-filter>  
  5. </service>  

6> 把应用中的aidl文件和所在package一起拷贝到客户端应用的src目录下,eclipse会自动在客户端应用的gen目录中为aidl文件同步生成IPersonService.java接口文件,接下来再把自定义类型文件和类型声明aidl文件及所在package一起拷贝到客户端应用的src目录下。
最后就可以在客户端应用中实现与远程服务的通信,代码如下:

[java] view plain copy print ?
  1. public class ClientActivityextends Activity {  
  2. private IPersonService personService;  
  3. @Override  
  4. public void onCreate(Bundle savedInstanceState) {  
  5. super.onCreate(savedInstanceState);  
  6. setContentView(R.layout.main);  
  7. this.bindService(new Intent("cn.jp.process.aidl.PersonService"),this.serviceConnection, BIND_AUTO_CREATE);//绑定到服务 
  8. }  
  9. @Override  
  10. protected void onDestroy() {  
  11. super.onDestroy();  
  12. this.unbindService(serviceConnection);//解除服务 
  13. }  
  14. private ServiceConnection serviceConnection =new ServiceConnection() {  
  15. @Override  
  16. public void onServiceConnected(ComponentName name, IBinder service) {  
  17. personService = IPersonService.Stub.asInterface(service);  
  18. try {  
  19. personService.save(new Person(56,"liming"));  
  20. } catch (RemoteException e) {  
  21. Log.e("ClientActivity", e.toString());  
  22. }  

 

=================================================================

ps:关于Parcelable

        先来说说Android对象序列化,在Android中序列化对象主要有两种方式,实现Serializable接口或是实现Parcelable接口。Serializable接口是JavaSE原生支持的,而Parcelable接口是Android所特有的,它的序列化和反序列化的效率均比Serializable接口高,而AIDL进行在进程间通信(IPC),就是需要实现这个Parcelable接口。

  Parcelable接口的作用:实现了Parcelable接口的实例,可以将自身的数据信息写入一个Parcel对象,也可以从parcel中恢复到对象的状态。而Parcel就是完成数据序列化写入的载体。

  上面提到Parcel,再来聊聊Parcel是什么?Android系统设计之初,定位就是针对内存受限的设备,因此对性能要求更好,所以系统中采用进程间通信(IPC)机制,必然要求性能更优良的序列化方式,所以Parcel就被设计出来了,其定位就是轻量级的高效的对象序列化机制与反序列化机制。如果读一下Android的底层代码,会发现Parcel是使用C++实现的,底层直接通过Parcel指针操作内存实现,所以它的才更高效。

        简单来说,Parcelable通过writeToParcel()方法,对复杂对象的数据写入Parcel的方式进行对象序列化,然后在需要的时候,通过其内定义的静态属性CREATOR.createFromParcel()进行反序列化的操作。Parcelable对Parcel进行了包装,其内部就是通过操作Parcel进行序列化与反序列化的。

        小结:1、使用writeToParcel()方法进行序列化,通过CREATOR.createFromParcel进行反序列化,它们都传递一个Parcel类型的对象,这里要注意的是两个方法中Parcel对象的writeXxx()和readXxx()方法的顺序必须一致,因为一般序列化数据是以链的形式序列化的,如果顺序不对,反序列化的数据会出错。2、进程间传递的数据必定是被序列化过的,否则无法传递。而对于那些AIDL默认允许传递的数据类型(int、double、String、List等),它们其实内部已经实现了序列化,所以无需我们再去指定序列化规则。但是对于复杂类型对象而言,系统无法知道如何去序列化与反序列化,所以需要我们指定规则。

 

你可能感兴趣的:(Android IPC 系列(2.3):AIDL)