一、IPC
IPC是Inter-Process Communication的缩写。
线程是CPU调度的最小单元。进程一般指一个执行单元。
多进程会导致以下几方面的问题:
1.静态成员和单例模式完全失效。
2.线程同步机制完全失效。
3.SharedPreferences的可靠性下降。
4.Application 会多次创建。
二、Serializable接口和Parcelable接口
Serializable接口:
其中Serializable接口,最好要有SerialVersionUID。
序列化的时候系统会把当前类的SerialVersionUID写入序列化的文件中,反序列的时候会去检测文件中的serialVersion是否一致。静态成员变量属于类,不属于类对象,所以不会参与序列化。同时,标示了transient的成员变量也不在序列化之列。
parcelable接口:
parcelable也是一个接口,只要实现这个接口,一个类的对象就可以实现序列化并可以通过Intent和Binder传递。
系统为我们提供了许多实现了Parcelable接口的类,他们都是可以直接序列化的,比如Intent、Bundle、Bitmap等。对于List和Map也可以实现序列化,前提是他们里面每个元素都是可序列化的。
三、Binder
Binder是Android中的一个类,它实现了IBinder接口。从IPC角度来说,Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder。
Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager等等)的桥梁。
从Android应用层来说,Binder是客户端和服务端进行通信的媒介。
当Bindler的工作方式:
服务端会返回一个包含了服务端业务调用的Bindler对象,通过这个Bindler对象,客服端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。
Binder主要用在Service中,包括AIDL和Messenger,其中普通Service中的Binder不涉及进程间通信。 使用AIDL来分析实现Bindler的工作机制。
它声明了一个内部类Stub,这个Stub就是一个Binder类,当客户端和服务端都位于同一个进程时,方法调用不会走跨进程的transact过程。而当两者位于不同进程时,方法调用需要走transact,这个逻辑由Stub的内部代理类Proxy来完成。AIDL核心就是它的内部类Stub和Stub的内部代理类Proxy。
四、Android中的IPC方式
Bundle:
四大组件中的三大组件(Activity、Service、Receiver)都是支持在Intent中传递Bundle数据的。因为Bundle实现了Parcelable接口。
文件共享:
文件共享也是一种进程间通信方式,两个进程通过读/写同一个文件来交换数据。
文件共享除了可以交换一些文本信息外,我们还可以序列化一个对象到文件系统中,同时从另一个进程恢复这个对象。
Android系统基于Linux,使得其并发读/写文件可以没有限制地进行。
比如SharedPreferences,SharedPreferences是Android中提供的轻量级存储方案,它通过键值对的方式来储存数据,在底层实现上它采用XML文件来存储键值对,每个应用的SharedPreferences文件都可以在当前包所在的data目录下查看到。系统对它的读/写有一定的缓存策略,即在内存中会有一份SharedPreferences文件的缓存,因此在多进程模式下,系统对它的读/写就变得不可靠。
如果需要保证数据安全,就需要加锁。
Messager:
Messger是一种轻量级的IPC方案,它的底层实现是AIDL。
Mesager的使用方法很简单。
首先在服务端创建一个Service来处理客户端的连接请求,同时创建一个Handler并通过它来创建一个Messager对象,然后在Service的onBind中返回这个Message对象底层的Bindler即可。
其次在客户端要绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messager,通过这个Messager就可以向服务端发送消息。如果需要服务端能够回应客户端,就和服务端一样,我们还需要创建一个Handler并创建一个新的Messager,并把这个messager对象通过Message的replyTo参数传给服务端,服务端通过这个replyTo参数就可以回应客户端。
AIDL:
AIDL(Android Interface Definition Language)是一种 IDL 语言,用于生成可以在 Android 设备上两个进程之间进行进程间通信(IPC)的代码。
首先在服务端创建一个Service用来监听客户端的连接请求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在Service中实现这个AIDL接口即可。
其次在客户端绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了。
最后AIDL接口的创建,在AIDL文件中,并不是所有的数据类型都是可以使用的。只有以下数据是可以的。
基本数据类型;
String和CharSequence;
List:只支持ArrayList,里面的每个元素都必须能够被AIDL支持;
Map:只支持HashMap,里面的每个元素都必须被AIDL支持,包括key和value;
Parcelable:所有实现了Parcelable接口的对象;
AIDL:所有的AIDL接口本身也可以在AIDl文件中使用。
AIDL中除了基本数据类型,其他类型的参数必须标上in、out、inout。
AIDL接口中只支持方法,不支持声明静态常量。
AIDL的包结构在服务端和客户端要保持一致,否则运行会出错,这是因为客户端需要反序列化服务端中和AIDL接口相关的所有类,如果类的完整路径不一样的话,就无法成功反序列化。
RemoteCallbackList是系统专门提供的用于删除跨进程listener的接口。RemoteCallbackList是一个泛型,支持管理任意的AIDL接口。实现方案是采用HashMap。
当客户端解注册的时候,我们只要遍历服务端所有的listener,找出那个和解注册listener具有相同Binder对象的服务端listener并把它删掉即可。RemoteCallbackList就是完成上述功能。
RemoteCallbackList使用方式:
register,unregister,同时beginBroadcast和finishBroadcast。他们俩必须配对使用。
ContentProvider:
ContentProvider是Android中提供的专门用于不同应用间进行数据共享的方式。其底层实现也是Binder。
需要注意以下细节:
1.CRUD(创建、读取、更新、删除)操作
2.防止SQL注入
其中SQL注入(SQL注入是一种常见的安全威胁,它允许攻击者通过向应用程序输入恶意构建的数据,向数据库系统发送错误的查询命令,以此来获取未授权的信息、修改数据、甚至控制整个数据库系统)
3.权限控制
Socket:
我们通过socket来实现进程间的通信。Socket也称为“套接字”,是网络通信中的概念,它分为流式套接字和用户数据报套接字两种,分别对应于网络的传输控制层中的TCP和UDP协议。
Binder连接池:
AIDL通信大致流程:
首先创建一个Service和一个AIDL接口,接着创建一个类继承自AIDL接口中的Stub类并实现Sub中的抽象方法,在Service的onBind方法中返回这个类的对象,然后客户端就可以绑定服务端Service,建立连接以后就可以访问远程服务端的方法了。
Binder连接池创建远程Service并实现IBinderPool,queryBinder的具体实现:
当Binder连接池连接上远程服务时,会根据不同模块的标识即binderCode返回不同的Binder对象,通过这个Binder对象所执行的操作全部发生在远程服务端。
选用合适的IPC方式
名称 | 优点 | 缺点 | 适用场景 |
Bundle | 简单易用 | 只能传输Bundle支持的数据类型 | 四大组件间的进程间通信 |
文件共享 | 简单易用 | 不适合高并发场景,并且无法做到进程间的即时通信 | 无并发访问情形,交换简单的数据实时性不高的场景 |
AIDL | 功能强大,支持一对多并发通信,支持实时通信 | 使用稍复杂,需要处理好线程同步 | 一对多通信且有RPC需求 |
Messenger | 功能一般,支持一对多串行通信,支持实时通信 | 不能很好处理高并发情形,不支持RPC,数据通过Message进行传输,因此只能传输Bundle支持的数据类型 | 低并发的一对多即时通信,无RPC需求,或者无须要返回结果的RPC需求 |
ContentProvider | 在数据源访问方面功能强大,支持一对多并发数据共享,可通过Call方法扩展其他操作 | 可以理解为受约束的AIDL,主要提供数据源的CRUD操作 | 一对多的进程间的数据共享 |
Socket | 功能强大,可以通过网络传输字节流,支持一对多并发实时通信 | 实现细节稍微有点繁琐,不支持直接的RPC | 网络数据交换 |
其中RPC为远程过程调用 。