1.IPC简介
IPC含义其实就是进程间通信或者跨进程通信,是指两个进程间数据交换的过程。进程和线程是包含与被包含的关系。
在安卓中最有特色的进程通信方式就是Binder了,除了这个还有Socket。
2.开启多进程模式
正常情况下,在android中多进程是指一个应用中存在多个进程的情况,因此这里补台轮两个应用指南的多进程情况。首先在安卓中使用多进程只有一种方法,那就是给四大组件在menifest文件中指定android:process的属性。进程名以“:”开头的进程属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中。不以:开头的属于全局进程,其他应用通过ShareUID方式可以和它跑在同一个进程中。我们知道android系统会为每一个应用分配一个唯一的UID,具有相同的uid的应用才能共享数据。我们这里讨论的是一个应用的多个进程,所以往往我们的进程process属性都用:remote代替了。
3.多进程模式的运行机制
每个进程都分配一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,所以使用多线程往往会造成如下几方面的问题:
静态成员和单例模式完全失效,因为都是不同的地址空间,同一个对象在内存分配上都会产生多分副本。
线程同步机制完全失效,其实和第一个问题类似,既然都不是一块内存了,那么不管锁对象还是锁全局类都无法保证同步了。
SharePreferences的可靠性下降,因为它不支持两个进程同时去执行写操作,否则会导致一定几率的数据丢失。
Application会多次创建,这也就证实来在多进程模式中,不同进程的组件的确会拥有独立的虚拟机,Application以及内存空间,这回给实际的开发带来许多困扰。或者我们也可以这么理解同一个应用间的多进程:它就相当于两个不同的应用采用了ShareUID的模式,这样能够更加直接地理解多进程模式的本质。
虽然多进程带来了很多问题,但我们不能因此就不去重视它,为了解决这个问题,系统提供了很多跨进程通信方法,虽然不能直接地共享内存,我们还是可以实现数据交互。实现跨进程通信的方式很多,比如通过intent来传递数据,共享文件和SharePreferences,基于Binder的Messenger和AIDL以及socket等,但是为了更好的理解各种ipc方式,我们需要先熟悉一些基础概念,比如序列化相关的serializable和parcelable接口,以及binder的概念,熟悉完这些基础概念以后再去理解各种IPC方式就比较简单了。
4.ipc基础概念的介绍
首先是serializable接口,和parcelable接口,在安卓平台上推荐后者。有时候在将对象序列化到存储设备中或对象序列化后通过网络传输则建议使用serializable。这里对于它两就不做详细介绍了,使用起来比较简单,继承接口,实现方法就好了。
重点需要理解的是 Binder:
直观来说,binder是安卓中的一个类,实现了IBinder接口。
从ipc角度来说它是安卓中一种跨进程通信方式
从安卓应用层来说,它是客户端和服务端进行通信的媒介,当bindservice的时候,它会返回一个包含了服务端业务调用的binder对象,通过这个binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。
安卓开发中,binder主要用在service中,包括AIDL和Messenger,其中普通service中的binder不涉及进程间通信,所以较为简单,无法触及binder的核心,而messenger的底层其实是AIDL, 所以这里我们选择用aidl来分析binder的工作机制。新建java包。。。。.aidl,新建3个文件,Book.java, Book.aidl, IBookManager.aidl.
上面三个文件中,book.java 就是一个表示图书信息的类,它实现了parcelable接口。Book.aidl是book类在aidl中的声明,IBookManager.aidl则是我们定义的一个接口,里面有两个方法。我们可以看到,尽管book类和ibookmanager位于同一个包,但是在ibookmanager中仍然要导入book类,这就是aidl的特殊之处,系统为ibookmanager.aidl生成一个binder类,对它进行了一些简单的分析,其实我们都可以不需要aidl直接写出这个binder类,然后在服务端只需创建一个bookmanageriml的对象实现这个binder类然后返回给客户端即可。AIDL文件的本质事系统为我们提供 类一种快速实现binder的工具,仅此而已。
到这里,ipc的基础知识就介绍完毕了,下面就开始进入正题,直面形形色色的进程间通信方式了。
5.安卓中的IPC方式
本节开始详细分析各种跨进程通信方式。具体方式有很多:
通过在intent中附加extras来传递信息
通过文件共享的方式来共享数据:
共享文件是一种不错的进程间通信方式,两个进程通过读/写同一个文件来交换数据,比如a进程把数据写入文件,然后b进程通过读取这个文件来获取数据。通过实验证明,的确恢复了之前存储的的对象的内容,这里之所以说内容,是因为反序列化得到的对象只是在内容上和序列化的对象是一样的,但它们本质上还是两个对象。文件共享方式适合在对数据同步要求不高的进程之间进行通信,并且要妥善处理并发读/写的问题。
使用messenger方式来跨进程通信:
messenger可以翻译为信使,顾名思义,通过它可以在不同进程中传递message对象,在messsage中放入我们需要传递的数据,就可以轻松实现了。它是一种轻量级的ipc方案,它的底层实现时aidl。由于它一次处理一个请求,因此在服务端我们不用考虑线程同步的问题。这是因为服务端不存在并发执行的情型。实现一个messenger有下面几个步骤,分为服务端和客户端。
服务端进程:
创建一个service来处理客户端的连接请求,同时创建一个handler并通过它来创建一个messenger对象,然后在service的onbind中返回这个messenger对象底层的binder即可。
客户端进程:
首先要绑定服务端的service,绑定成功后用服务端返回的ibinder对象创建一个messenger,通过这个messenger就可以向服务端发送message消息了。同理如果需要服务端能够回应客户端,就和服务端一样,我们需要创建一个handler 并创建一个新的messenger,并把这个messenger对象通过message的replyto参数传递给服务端,服务端通过这个replyto参数就可以获得客户端的messenger然后回应客户端。
使用AIDL:
上面我们介绍了messenger来进行进程间通信的方法,可以发现,messenger是以串行的方式来处理客户端的消息,如果大量的消息同时发送到服务端,服务端仍然只能一个一个的处理,如果有大量的并发请求,那么用messenger就不太合适了。同时messenger的作用主要是为了传递消息,很多时候我们可能需要跨进程调用服务端的方法,这种情形messenger就无法做到了,但是我们可以使用aidl来实现跨进程的方法调用。aidl也是messenger的底层实现,因此messenger本质也是aidl。这里先介绍使用aidl来进行进程间通信的流程,也是分为服务端和客户端。
服务端:
首先创建一个service 用来监听客户端的连接请求,然后创建一个aidl文件,将暴露给客户端的接口在这个aidl文件里声明,最后在service中实现这个aidl接口即可。
客户端:
首先需要绑定服务端的service,绑定成功后,服务端返回的binder对象转换成aidl接口所属的类型,接着调用aidl中的方法即可。
需要注意的地方,如果aidl文件中用到了自定义的parcelable对象,必须新建一个和它同名的aidl文件,并在其中声明它为parceable类型,前面基础知识有提到过,需要注意这个问题。aidl的接口只支持方法,不支持声明静态常量,这点区别于传统的接口。为了方便aidl的开发,建议把所有喝aidl相关的类和文件全部放入同一个包中,这样做的好处是当客户端是另外一个应用时,我们可以直接把包复制到客户端工程中,需要注意的是,aidl的包结构在服务端和客户端需要保持一致,否则运行会出错。(通过图书管的案例简单明了)
aidl的复杂性远不止这些,下面继续介绍aidl中常见的难点。假设有一种需求,用户不想时不时的去查询图书列表了,想实现,图书馆有新书就主动告诉读者。这就是一种典型的观察者模式(接口的回调)。我们就来模拟这种情形。关于RemoteCallBackList(来实现解注册的功能)的使用,请读者自行去查阅资料。用他的核心原因就是,binder会把客户端传过来的对象重新转化并生成一个新的对象。。。。。
还有如何在aidl中使用权限认证功能,这个也很简单,大家自己去查阅资料即可。
ContentProvider天生就支持跨进程访问的
下节内容继续
网络通信也是可以实现数据传递的,所以socket也可以实现ipc