Android IPC机制(三) Android中的IPC方式
使用Bundle
我们知道,四大组件中的三大组件(Activity,Service,Receiver)都是支持在Intent中传递Bundle数据的,由于Bundle实现了Parcelable接口,所以它可以方便的在不同的进程间传输,基于这一点,当我们在一个进程中启动了另一个进程的Activity,Service和Receiver,我们就可以在Bundle中附加我们需要传输给远程的信息并通过Intent发送出去,当然,我们传输的数据必须能够被序列化,比如基本类型,实现了Parcellable接口的对象,实现了Serializable接口的对象以及一些Android支持的特殊对象,具体内容可以看Bundle这个类,就可与看到所有它支持的类型,Bundle不支持的类型我们无法通过它在进程间传递数据。
使用文件共享
共享文件也是一种进程间通信方式,两个进程通过读/写同一个文件来交换数据,比如A进程把数据写入文件,B进程通过读取这个文件来获取数据,在Window中,一个文件如果被加了排斥锁将会导致其他线程无法对其进程访问,包括读和写,而由于Android系统基于Linux,使得其并发读写文件可以没有限制的进行,甚至两个线程同时对同一个文件进行写操作都是允许的,尽管可能出问题,通过文件交换数据很好使用,除了可以交换一些文本信息外,我们还可以序列化一个对象到文件系统中的同时从另一个进程中恢复这个对象。
通过文件共享这种方式来共享数据对文件格式是没有具体要求的,可以是文本文件,也可以是XML文件,只要读写双方约定数据格式即可,通过文件共享的方式也是有局限性,比如并发读写问题,如果并发读写,那么我们读出的内容就有可能不是最新的,如果是并发写的话就更严重了,因此我们要尽量避免并发写这种情况的发生或者考虑使用线程同步来限制多个线程的写操作,文件共享方式适合在数据同步要求不高的进程之间进行通信,并且要妥善处理并发读写
SharedPreferences是个特例,SharedPreferences是Android 中提供的轻量级存储方案,它通过键值对的方式来存储数据,在底层实现上它采用XML文件来存储键值对,每个应用的SharedPreferences文件都可以在当前包所在的data目录下查看到,一般来说,它的目录位于data/data/package name/shared_prefs目录下,其中package name表示的是当前应用的包名,从本质来说,SharedPreferences也属于文件的一种,但是由于系统对它的读写有一定的缓存策略,即在内存中会有一份SharedPreferences文件的缓存,因此在多进程模式中,系统对它的读写 就变得不可靠,当面对高并发的读写访问,SharedPreferences有很大几率会丢失数据,因此,不建议在进程间通信中使用SharedPreferences
使用Messenger
Messenger 可以翻译为信使,通过它可以在不同进程中传递Message对象,在Message中放入我们需要传递的数据,就可以轻松的实现数据的进程间传递了,Messenger是一种轻量级的IPC方案,它的底层实现是AIDL,我们大致看一下Messenger这个类的构造方法就明白了
Messenger的使用方法很简单,它对AIDL做了封装,使得我们可以更简单的进行进程间通信,同时,由于它一次处理一个请求,因此在服务端我们不用考虑线程同步的问题,还是因为服务端中不存在并发执行的情形,实现一个Messenger有如下步骤,分为服务端和客户端:
1 服务端进程
首先我们需要在服务端创建一个Service来处理客户端的连接请求,同时创建一个Handler 并通过它来创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可
2 客户端进程
客户端进程中,首先要绑定服务端的Service,绑定成功后用服务端返回的IBinder 对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,发消息类型为Message对象,如果需要服务端能够回应客户端,就和服务端一样,我们还需要创建一个Handler 并创建一个新的Messenger,并把这个Messenger 对象通过Message的replyTo参数传递给服务端,服务端通过这个replyTo参数就可以回应客户端。
客户端实现
在Messenger中进行数据传递必须将数据放入Message中,而Messenger和Message都实现了Parcelable接口,因此可以跨进程传输,简单来说,Message中所支持的数据类型就是Messenger 所支持的传输类型,实际上,通过Messenger来传输Message,Message中能使用的载体使用what,arg1 ,arg2 ,Bundle以及relpyTo,Message中的另一个字段object 在同一个进程中是很实用的,但是在进程间通信的时候,在Android 以后Object字段不支持跨进程通信,我们自定义的Parcelable对象是无法通过object 字段来传输的,这也导致了object 字段的实用性大大降低。
使用AIDL
Messenger是以串行的方式处理客户端发来的消息,如果大量的消息同时发送到服务端,服务端仍然只能一个个处理,如果大量的消息同时发送到服务端,服务端仍然只能一个个处理,如果有大量的并发请求,那么用Messenger就不太合适了,同时,Messenger的作用主要是为了传递消息,很对时候我们可能需要跨进程调用服务端的方法,这种情形用Messenger就无法做到了,但是我们可以使用AIDL来实现跨进程的方法调用,AIDL也是Messenger的底层实现,因此Messenger本质上也是AIDL,只不过系统为我们做了封装从而方便上层的调用而已。先介绍使用AIDL来进行进程间 通信的流程:
1 服务端
服务端首先要创建一个Service用来监听客户端的连接请求,然后创建一个AIDL文件,将暴漏给客户端的接口在这个AIDL文件中声明,最后在Service中实现这个AIDL接口即可
2 客户端
客户端所要做事情就稍微简单一些,首先需要绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了
3 AIDL接口的创建
首先是AIDL接口的创建,我们创建了一个后缀为AIDL的文件,在里面声明了一个接口和两个接口的方法
在AIDL文件中,并不是所有的数据类型都是可以使用的,那么到底AIDL文件支持那些数据类型呢?
1 基本数据类型(int long char boolean doubel等)
2 String和CharSequence
3 List :只支持ArrayList,里面每个元素都必须能够被AIDL支持
4 Map: 只支持HashMap,里面的每个元素都必须被AIDL支持,包括key 和value
5 Parcelable:所有实现了Parcelable接口的对象
6 AIDL:支持所有AIDL接口本身也可以在AIDL文件中使用
以上6种数据类型就是AIDL所支持的所有类型,其中自定义的Parcelable对象和AIDL对象必须要显示import进来,不管它们是否和当前的AIDL文件位于同一个包内,比如IBookManager.aidl这个文件,里面用到了Book这个类,这个类实现了Parcelable接口并且和IBookManager.aidl位于同一个包中,但是遵守AIDL的规范,我们仍要显示的import进来。
如果AIDL文件中用到了自定义的Parcelable对象,那么必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型。
AIDL中每个实现了Parcelable接口的类都需要按照上面那种方式去创建相应的AIDL文件并声明那个类为parcelable ,除此之外,AIDL中除了基本嫩数据类型,其他类型的参数必须标上方向:in,out 或者inout,in表示输入型参数,out表示输出型参数,inout表示输入输出型参数。
使用ContentProvider
ContentProvider是Android 中提供的专门用于不同应用间进行数据共享的方式,从这一点来看,它天生就适合进程间通信,和Massenger一样,ContentProvider的底层实现同样也是Binder,由此可见,Binder 在Android 系统中是何等的重要,虽然ContentProvider的底层实现是Binder,但是她的使用过程要比AIDL简单得多,这是因为系统已经为我们做了封装,使得我们无须关心底层细节即可轻松实现IPC,ContentProvider 虽然使用起来很简单,包括自己创建一个ContentProvider 也不是难事,尽管如此,它的细节还是相当多,比如CRUD操作,防止SQL注入和权限控制等
ContentProvider主要以表格的形式来组织数据,并且可以包含多个表,对于每个表格来说,他们具有列和行的层次性,行往往对应一条数据,而列对应一条记录中的一个字段,这点和数据库很类似,除了表格的形式不同,ContentProvider还支持文件数据,比如图片,视频等,文件数据和表格数据的结构不同,因此处理这类数据时可以在ContentProvider中返回文件的句柄给外界从而让文件来访问ContentProvider。
使用Socket
Socket也称为套接字,是网络通信中的概念,它分为流式套接字和用户数据报套接字两种,分别对应网络的传输控制层中的TCP和UDP协议,TCP协议是面向连接的协议,提供稳定的双向通信功能,TCP连接的建立需要经过三次握手才能完成,为了提供稳定的数据传输功能,其本身提供了超时重传机制,因此具有很高的稳定性,而UDP是无连接的,提供不稳定的单项通信功能,当然UDP也可以实现双向通信功能,在性能上,UDP具有更好的效率,其缺点是不保证数据一定能够正确传输,尤其是在网络拥塞的情况下。两个进程可以通过Socket来实现信息的传输,Socket本身可以支持传输任意字节流
使用Scoket来进行通信,有两点需要注意,首先要声明权限
首先要注意的是不能在主线程中访问网络,这会导致我们的程序无法的Android4.0 以上的设备中运行,而且进程网络操作是耗时的,如果放在主线程会影响程序的响应效率,从这方面来说,也不应该在主线程中访问网络
服务端:
客户端: