IPC——android进程间通信

来源:互联网 发布:微信朋友圈封面知乎 编辑:程序博客网 时间:2024/05/21 19:37

[+]

一,什么是IPC

IPC:inter-process communication,进程间通信或者跨进程通信。window通过剪贴板,管道等进行进程间通信。Linux通过命名管道,共享内存,信号量等进行进程间通信。android有特色的是Binder。在android进程通信可以有以下方式:aidl,socket通信,使用Bundle,使用contentprovider,使用Messenger,使用文件共享。

二,android的序列化

在android中能用于进程间通信的对象必须是序列化的。序列的方式有两种,一种是实现Serializable接口,一种是实现Parcelable。Serializable是java提供的序列化方法,实现很简单,只要一个类实现了它就完成了序列化。可用于将对象序列化到存储设备或序列化后通过网络传输。但它的效率不高,这里重点介绍Parcelable,它是android自己的实现序列化方法,先看实现Parcelable的代码:

只要一个类实现了这个接口,就可以实现序列化并可以通过Intent和Binder传递。要实现以下方法
(1).writeToParcel(out,flags):实现序列化功能,一系列write。如下
out.writeInt(userId);
out.writeParcelable(book,0);//传另一个序列化类
(2).CREATOR:反序列化,一系列的read方法:如下
userId = in.readInt();
book = in.readParcelable(Thread.currentThread().getContextClassLoader());
(3).decribeContents:内容描述,几乎所有情况都返回0.
完整代码如下
[java] view plain copy
  1. public class Book implements Parcelable {  
  2.   
  3.     public int bookId;  
  4.     public String bookName;  
  5.     public Book() {  
  6.   
  7.     }  
  8.   
  9.     public Book(int bookId, String bookName) {  
  10.         this.bookId = bookId;  
  11.         this.bookName = bookName;  
  12.     }  
  13.   
  14.     public int describeContents() {  
  15.         return 0;  
  16.     }  
  17.   
  18.     public void writeToParcel(Parcel out, int flags) {  
  19.         out.writeInt( bookId);  
  20.         out.writeString( bookName);  
  21.     }  
  22.   
  23.     public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {  
  24.         public Book createFromParcel (Parcel in) {  
  25.             return new Book(in);  
  26.         }  
  27.   
  28.         public Book[] newArray( int size) {  
  29.             return new Book[size];  
  30.         }  
  31.     };  
  32.   
  33.     private Book(Parcel in) {  
  34.         bookId = in.readInt();  
  35.         bookName = in.readString();  
  36.     }  
  37.   
  38.     @Override  
  39.     public String toString() {  
  40.         return String.format("[bookId:%s, bookName:%s]" , bookId , bookName);  
  41.     }  
  42.   
  43. }  


系统还提供了一些实现了Parcelable接口的类,如Intent,Bundle,Bitmap.同时List和Map也可以序列化,前提是它们里所有元素都 可以序列化。
Parcelable效率比Serializable高,在内存序列化上用它合适。但在将对象序列化到存储设备或序列化后通过网络传输则建议用Serializable,比较方便。

二,Binder的介绍

Binder用于完成进程间的通信(IPC)。Binder是工作在Linux层,属于一个驱动,这个驱动不需要驱动,Binder代码运行在内核态,调用Binder是系统进行调用。

Binder是一种架构,这种架构提供服务端接口,Binder 接口,客户端接口三个模块。一个Binder 服务端就是一个Binder对象,该对象一旦创建,就会启动一个隐藏线程,该线程会接Binder 驱动发送的消息。收到消息会发调用 onTransact()方法,并按照参数执行不同的服务代码,onTransact() 的参数来源是客户端调用 transact(),若transact() 有固定的输入,onTransact()就有固定的输出 。
一个服务端被创建,会创建一个mRemote对象,它的类型也是Binder类,客户端要访问远程时是通过它。它也重载了transact()方法,重载内容如下:
1,以线程消息通信模式,向服务端发送客户端传递过来的参数。
2.挂起当前线程,并等待服务端线程执行完指定服务函数后通知。
3.接收服务端通知并执行客户端线程,并返回到客户端代码区。
它的调用图如下:

接下来我们来看看如何使用它。
新建两个文件Book.aidl,IBookManager.aidl,其中Book.aidl是上面序列化后文件的一个声明,主意名称要和序列化的类的名称一样。内容如下:
[java] view plain copy
  1. package com.lxj.aidl;  
  2.   
  3. parcelable Book;  
再看下IBookManager.aidl,这个demo主要实现两个方法,所以都需要这此声明:
[java] view plain copy
  1. package com.lxj.aidl;  
  2.   
  3. import com.lxj.aidl.Book;  
  4.   
  5. interface IBookManager {  
  6.      List<Book> getBookList();  
  7.      void addBook(in Book book);  
  8.   
  9. }  
除了基本数据类型外,其它数据类型都要以in out inout来标识,in表示输入,out表示输出,inout即可表示输入也可表示输出。
这时系统会自动生成一个IBookManager.java文件,内容如下:
[java] view plain copy
  1. package com.lxj.aidl;  
  2. public interface IBookManager extends android.os.IInterface  
  3. {  
  4. /** Local-side IPC implementation stub class. */  
  5. public static abstract class Stub extends android.os.Binder implements com.lxj.aidl.IBookManager  
  6. {  
  7. private static final java.lang.String DESCRIPTOR = "com.lxj.aidl.IBookManager";  
  8. /** Construct the stub at attach it to the interface. */  
  9. public Stub()  
  10. {  
  11. this.attachInterface(this, DESCRIPTOR);  
  12. }  
  13. /** 
  14.  * Cast an IBinder object into an com.lxj.aidl.IBookManager interface, 
  15.  * generating a proxy if needed. 
  16.  */  
  17. public static com.lxj.aidl.IBookManager asInterface(android.os.IBinder obj)  
  18. {  
  19. if ((obj==null)) {  
  20. return null;  
  21. }  
  22. android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);  
  23. if (((iin!=null)&&(iin instanceof com.lxj.aidl.IBookManager))) {  
  24. return ((com.lxj.aidl.IBookManager)iin);  
  25. }  
  26. return new com.lxj.aidl.IBookManager.Stub.Proxy(obj);  
  27. }  
  28. @Override public android.os.IBinder asBinder()  
  29. {  
  30. return this;  
  31. }  
  32. @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException  
  33. {  
  34. switch (code)  
  35. {  
  36. case INTERFACE_TRANSACTION:  
  37. {  
  38. reply.writeString(DESCRIPTOR);  
  39. return true;  
  40. }  
  41. case TRANSACTION_getBookList:  
  42. {  
  43. data.enforceInterface(DESCRIPTOR);  
  44. java.util.List<com.lxj.aidl.Book> _result = this.getBookList();  
  45. reply.writeNoException();  
  46. reply.writeTypedList(_result);  
  47. return true;  
  48. }  
  49. case TRANSACTION_addBook:  
  50. {  
  51. data.enforceInterface(DESCRIPTOR);  
  52. com.lxj.aidl.Book _arg0;  
  53. if ((0!=data.readInt())) {  
  54. _arg0 = com.lxj.aidl.Book.CREATOR.createFromParcel(data);  
  55. }  
  56. else {  
  57. _arg0 = null;  
  58. }  
  59. this.addBook(_arg0);  
  60. reply.writeNoException();  
  61. return true;  
  62. }  
  63. }  
  64. return super.onTransact(code, data, reply, flags);  
  65. }  
  66. private static class Proxy implements com.lxj.aidl.IBookManager  
  67. {  
  68. private android.os.IBinder mRemote;  
  69. Proxy(android.os.IBinder remote)  
  70. {  
  71. mRemote = remote;  
  72. }  
  73. @Override public android.os.IBinder asBinder()  
  74. {  
  75. return mRemote;  
  76. }  
  77. public java.lang.String getInterfaceDescriptor()  
  78. {  
  79. return DESCRIPTOR;  
  80. }  
  81. @Override public java.util.List<com.lxj.aidl.Book> getBookList() throws android.os.RemoteException  
  82. {  
  83. android.os.Parcel _data = android.os.Parcel.obtain();  
  84. android.os.Parcel _reply = android.os.Parcel.obtain();  
  85. java.util.List<com.lxj.aidl.Book> _result;  
  86. try {  
  87. _data.writeInterfaceToken(DESCRIPTOR);  
  88. mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);  
  89. _reply.readException();  
  90. _result = _reply.createTypedArrayList(com.lxj.aidl.Book.CREATOR);  
  91. }  
  92. finally {  
  93. _reply.recycle();  
  94. _data.recycle();  
  95. }  
  96. return _result;  
  97. }  
  98. @Override public void addBook(com.lxj.aidl.Book book) throws android.os.RemoteException  
  99. {  
  100. android.os.Parcel _data = android.os.Parcel.obtain();  
  101. android.os.Parcel _reply = android.os.Parcel.obtain();  
  102. try {  
  103. _data.writeInterfaceToken(DESCRIPTOR);  
  104. if ((book!=null)) {  
  105. _data.writeInt(1);  
  106. book.writeToParcel(_data, 0);  
  107. }  
  108. else {  
  109. _data.writeInt(0);  
  110. }  
  111. mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);  
  112. _reply.readException();  
  113. }  
  114. finally {  
  115. _reply.recycle();  
  116. _data.recycle();  
  117. }  
  118. }  
  119. }  
  120. static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);  
  121. static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);  
  122. }  
  123. public java.util.List<com.lxj.aidl.Book> getBookList() throws android.os.RemoteException;  
  124. public void addBook(com.lxj.aidl.Book book) throws android.os.RemoteException;  
  125. }  
asInterface:用于将服务端的Binder对象转换成客户端所需的AIDL对象,同一进程返回的是服务端的Stub,不同进程返回系统封闭后的Stub.proxy对象。
asBinder:用于返回当前 的Binder对象。
onTransact(code,data,reply,flags):运行在服务端的Binder线程池中,客户端发出的请求会在此处理。code可以确定客户端的请求目标方法是什么,data取出参数,运行完把结果赋给reply。如果返回false,则客户端请求会失败(可用于权限判断)。
getBookList:首先创建输入输出的Parcel对象_data,_reply。接着调用transact方法来发起RPC(远程过程调用)请求,同时挂起线程,然后服务端onTransact会调用,直到RPC执行完,当前线程继续执行,并从_reply取出RPC结果。返回。

四,AIDL进程间调用

aidl是最常见的进程调用方式。我们先创建两个应用,模拟进程间通信(不同应用)。客户端的代码如下:
[java] view plain copy
  1. protected void onCreate(Bundle savedInstanceState) {  
  2.         super.onCreate(savedInstanceState);  
  3.         setContentView(R.layout.activity_main);  
  4.         Intent intent = new Intent("com.lxj.servicetest.MyAIDLService");  
  5.         bindService(intent, connection, Context.BIND_AUTO_CREATE);  
  6.     }  
  7.     private ServiceConnection connection = new ServiceConnection() {  
  8.           
  9.         @Override  
  10.         public void onServiceDisconnected(ComponentName name) {  
  11.             // TODO Auto-generated method stub  
  12.               
  13.         }  
  14.           
  15.         @Override  
  16.         public void onServiceConnected(ComponentName name, IBinder service) {  
  17.             // TODO Auto-generated method stub  
  18.             IBookManager bookManager = IBookManager.Stub.asInterface(service);  
  19.             try {  
  20.                 List<Book> list = bookManager.getBookList();  
  21.                 Log.e("list", list.toString());  
  22.             } catch (RemoteException e) {  
  23.                 // TODO Auto-generated catch block  
  24.                 e.printStackTrace();  
  25.             }  
  26.         }  
  27.     };  
通过bindService绑定服务端Binder,然后通过IBookManager.Stub.asInterface()来获得服务端返回的AIDL对象,就可以调用到服务端的方法(getBookList)。再看下服务端代码:
[java] view plain copy
  1. public class MyBookService extends Service {  
  2.     private CopyOnWriteArrayList<Book> mBooks = new CopyOnWriteArrayList<Book>();  
  3.       
  4.     @Override  
  5.     public void onCreate() {  
  6.         // TODO Auto-generated method stub  
  7.         super.onCreate();  
  8.         mBooks.add(new Book(1,"java虚拟机"));  
  9.         mBooks.add(new Book(2,"android疯狂讲义"));  
  10.     }  
  11.   
  12.     private Binder mBinder = new IBookManager.Stub() {  
  13.         //虽然IBinder只能传输List,但是服务端之所以可以用CopyOnWriteArrayList是因为它会自动在传输时转化为list  
  14.         @Override  
  15.         public List<Book> getBookList() throws RemoteException {  
  16.             // TODO Auto-generated method stub  
  17.             return mBooks;  
  18.         }  
  19.           
  20.         @Override  
  21.         public void addBook(Book book) throws RemoteException {  
  22.             // TODO Auto-generated method stub  
  23.             mBooks.add(book);  
  24.         }  
  25.     };  
  26.   
  27.     @Override  
  28.     public IBinder onBind(Intent intent) {  
  29.         // TODO Auto-generated method stub  
  30.         return mBinder;  
  31.     }  
  32.   
  33. }  
自定义了一个Service并在list中添加两本书,所以客户端就能在控制台上打印出这两本书。注意要在AndroidFest.xml添加上Service的声明:
[java] view plain copy
  1. <service  
  2.          android:name="com.lxj.aidl.MyBookService"  
  3.          >  
  4.          <intent-filter>    
  5.          <action android:name="com.lxj.servicetest.MyAIDLService"/>    
  6.      </intent-filter>   
  7.      </service>  

四,Messenger进程间通信

android提供Messenger可以实现不同进程间的信息传递,相比AIDL它的调用简单很多,但是它是有缺点的,它只能串行处理发送的信息,如里信息过多就会阻塞。其次它只能传输基本数据类型,What,arg1,Bundle等,并不能传输自定义的对象。Messenger是一种种轻量级的IPC方案,一次处理一个请求。
1.服务端:
创建一个Service来处理客户端连接请求,同时创建一个Handler并通过它来创建一个Message对象,然后在OnBinder中返回这个Binder。
2.客户端:
首先绑定服务端Service,通过返回的IBinder创建一个messager。这个就可以给服务端发消息了,如果需要服务端返回,则也要跟服务端一个创建Handler,通过Message的replyTo参数传递给服务端。
先看下客户端的代码:
先看客户端代码:
[java] view plain copy
  1. public class MainActivity extends Activity {  
  2.     TextView txvTextView ;  
  3.     private Messenger mService;  
  4.     private Messenger clientMessenger = new Messenger(new MessengerHandler());  
  5.     private static class MessengerHandler extends Handler{  
  6.   
  7.         @Override  
  8.         public void handleMessage(Message msg) {  
  9.             super.handleMessage(msg);  
  10.             switch(msg.what){  
  11.             case 1:  
  12.                 Log.e("msg from service",  msg.getData().getString("reply"));  
  13.                 break;  
  14.              default:  
  15.             super.handleMessage(msg);  
  16.             }  
  17.         }  
  18.           
  19.     }  
  20.     private ServiceConnection connection = new ServiceConnection(){  
  21.   
  22.         @Override  
  23.         public void onServiceConnected(ComponentName name, IBinder service) {  
  24.             // TODO Auto-generated method stub  
  25.              mService = new Messenger(service);  
  26.             Message msgMessage = Message.obtain(null,0);  
  27.             Bundle b = new Bundle();  
  28.             b.putString("client""Hi,i am client");  
  29.             msgMessage.setData(b);  
  30.             msgMessage.replyTo = clientMessenger;  
  31.             try {  
  32.                 mService.send(msgMessage);  
  33.             } catch (RemoteException e) {  
  34.                 // TODO Auto-generated catch block  
  35.                 e.printStackTrace();  
  36.             }  
  37.         }  
  38.   
  39.         @Override  
  40.         public void onServiceDisconnected(ComponentName name) {  
  41.             // TODO Auto-generated method stub  
  42.               
  43.         }  
  44.   
  45.   
  46.     };  
  47.     @Override  
  48.     protected void onCreate(Bundle savedInstanceState) {  
  49.         super.onCreate(savedInstanceState);  
  50.         setContentView(R.layout.activity_main);  
  51.         txvTextView = (TextView)super.findViewById(R.id.txv);  
  52.         Intent i = new Intent(this,MessengerService.class);  
  53.         bindService(i, connection, Context.BIND_AUTO_CREATE);  
  54.     }  
  55.   
  56.        @Override  
  57.         protected void onDestroy() {  
  58.             unbindService(connection);  
  59.             super.onDestroy();  
  60.         }  
  61.   
  62. }  
看到上面的代码我们可以很清楚的看到,Messenger也是用AIDL实现进程间通信的。通过Messenger发送消息到服务端,handler中获取服务端返回的replyto。再看下服务端代码:
[java] view plain copy
  1. public class MessengerService extends Service{  
  2.     private final Messenger mMessenger = new Messenger(new MessengerHandler());  
  3.     private static class MessengerHandler extends Handler{  
  4.   
  5.         @Override  
  6.         public void handleMessage(Message msg) {  
  7.             super.handleMessage(msg);  
  8.             switch(msg.what){  
  9.             case 0:  
  10.                 Log.e("msg from client",  msg.getData().getString("client"));  
  11.                 Message message = Message.obtain(null,1);  
  12.                 Messenger messenger = msg.replyTo;//从客户端得到的messenger,用于在客户端中显示  
  13.                 Bundle bundle = new Bundle();  
  14.                   
  15.                 bundle.putString("reply""Hi,i am service");  
  16.                 message.setData(bundle);  
  17.                 try {  
  18.                     Thread.sleep(1000);  
  19.                     messenger.send(message);  
  20.                 } catch (Exception e) {  
  21.                     // TODO Auto-generated catch block  
  22.                     e.printStackTrace();  
  23.                 }  
  24.                 break;  
  25.              default:  
  26.             super.handleMessage(msg);  
  27.             }  
  28.         }  
  29.           
  30.     }  
  31.   
  32.     @Override  
  33.     public IBinder onBind(Intent intent) {  
  34.         // TODO Auto-generated method stub  
  35.         return  mMessenger.getBinder();  
  36.     }  
  37.   
  38. }  
这里就是获取到信息后再把信息发送回客户端。完成了不同进程的信息传递。
这里只是简单介绍两种IPC方式,还有很多常用方式如socket通信,ContentProvider等都可以实现IPC。本文只是总结下自己对IPC的认识。
0 0