Android进程通信
一、进程
Android中进程分为5种:
1 Foreground process 前端进程
目前显示在屏幕上和用户交互的进程。
比如:
顶层可交互的Activity(已执行onResume);
有个Service,并绑定到跟用户正在交互的Activity;
在Service里调用了startForeground函数;
正在执行onReceive的BroadcastReceiver。
2 Visible process 可见进程
没有任何前台组件,但仍能影响用户在屏幕上看到东西。
比如:
如果一个Activity在一个对话框运行之后仍然是可视的;
输入法弹出时。
3 Service process 服务进程
服务进程不会直接为用户所见。
比如:
在后台播放mp3或者下载东西。
4 Background process 后台进程
比如:
Activity执行了onStop。
5 Empty process 空进程
优先级层次
Android进程层次.pngRPC 和 IPC
RPC:Remote Procedure Call,即远程过程调用,它是一种通过网络从远程计算机程序上请求服务,在不需要了解底层网络技术的协议下,即可获取计算机进程中的数据。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
RPC在OSI网络通信7层模型中,位于传输层与应用层之间,即位于会话层。
RPC可以说客户端调用服务端的接口的过程,是面向接口的编程。
RPC与IPC的关系:
Android 利用远程过程调用 (RPC) 提供了一种进程间通信 (IPC) 机制,通过这种机制,由 Activity 或其他应用组件调用的方法将(在其他进程中)远程执行,而所有结果将返回给调用方。
这就要求把方法调用及其数据分解至操作系统可以识别的程度,并将其从本地进程和地址空间传输至远程进程和地址空间,然后在远程进程中重新组装并执行该调用。然后,返回值将沿相反方向传输回来。Android 提供了执行这些 IPC 事务所需的全部代码,因此您只需集中精力定义和实现 RPC 编程接口即可。
要执行 IPC,必须使用 bindService() 将应用绑定到服务上。
也就是说,RPC在的Android具体体现,是依赖 bindService()的方式,在onBind方法将服务端的计算结果返回给客户端(Activity等组件)的过程。
IPC:Inter-Process Communication,即进程间通信,是指进程间数据交互的过程。
Android底层是基于Linux,而Linux基于安全考虑,是不允许两个进程间直接操作对方的数据,这就是进程隔离。
在Linux系统中,虚拟内存机制为每个进程分配了线性连续的内存空间,操作系统将这种虚拟内存空间映射到物理内存空间,每个进程有自己的虚拟内存空间,进而不能操作其他进程的内存空间,每个进程只能操作自己的虚拟内存空间,只有操作系统才有权限操作物理内存空间。进程隔离保证了每个进程的内存安全,但是在大多数情形下,不同进程间的数据通讯是不可避免的,因此操作系统必须提供跨进程通信机制。
Android应用中使用多进程:
-
在AndroidManifest.xml中声明组件时,用android:process属性来指定进程。
-
不指定process属性,则默认运行在主进程中,主进程名字为包名。
-
android:process = package:remote,将运行在package:remote进程中,属于全局进程,其他具有相同shareUID与签名的APP可以跑在这个进程中。
-
android:process = :remote ,将运行在默认包名:remote进程中,而且是APP的私有进程,不允许其他APP的组件来访问。
多进程引发的问题
-
静态成员和单例失效:每个进程保持各自的静态成员和单例,相互独立。
-
线程同步机制失效:每个进程有自己的线程锁。
-
SharedPreferences可靠性下降:不支持并发写,会出现脏数据。
-
Application多次创建:不同进程跑在不同虚拟机,每个虚拟机启动会创建自己的Application,自定义Application时生命周期会混乱。
综上,不同进程拥有各自独立的虚拟机、Application、内存空间,不同进程访问同一个类的对象会有不同的副本,由此引发一系列问题。
二、进程间通信
虽然Android是基于Linux,但并不能继承Linux中的进程通信的方式,Android有着自己进程间通信方式。
1 Bundle (四大组件间)
可传递基本类型、String、实现了Serializable或Parcelable接口的数据结构。
Serializable是Java的序列化方法,Parcelable是Android的序列化方法。前者代码量少(仅一句),但I/O开销较大,一般用于输出到磁盘或网卡;后者实现代码多,效率高,一般用户内存间序列化和反序列化传输。
详情请移步:
2 文件共享
对同一个文件先后写读,从而实现传输,Linux机制下,可以对文件并发写,所以要注意同步。顺便一提,Windows下不支持并发读或写。
3 Messenger(基于Binder)
Messenger是基于AIDL实现的,通过Message对象进行跨进程通信,类似于Handler发送消息实现线程间通信。服务端(被动方)提供一个Service来处理客户端(主动方)连接,维护一个Handler来创建Messenger,在onBind时返回Messenger的binder。
双方用Messenger来发送数据,用Handler来处理数据。Messenger处理数据依靠Handler,所以是串行的,也就是说,Handler接到多个message时,就要排队依次处理。
此外,还支持记录客户端对象的Messenger,然后可以实现一对多的通信;甚至作为一个转接处,任意两个进程都能通过服务端进行通信。
与 AIDL 比较:
- 当需要执行 IPC 时,为接口使用 Messenger 要比使用 AIDL 实现更加简单,因为 Messenger 会将所有服务调用排入队列,而纯粹的 AIDL 接口会同时向服务发送多个请求,服务随后必须应对多线程处理。
- 对于大多数应用,服务不需要执行多线程处理,因此使用 Messenger 可让服务一次处理一个调用。如果服务必须执行多线程处理,则应使用 AIDL 来定义接口。
使用步骤
服务端:
- 创建一个Handler对象,并实现handleMessage方法,用于接收和处理来自客户端的消息。
- 创建一个Messenger作为送信人,封装Handler。
- 用Messenger的getBinder()方法获取一个IBinder对象,通过onBind返回给客户端。
客户端:
- 在Activity中绑定服务。
- 创建ServiceConnection并在其中使用 IBinder 将 Messenger实例化。
- 使用Messenger向服务端发送消息。
- 解绑服务。
- 服务端中在 handleMessage() 方法中接收每个 Message。
以上实现的仅仅是单向通信,即客户端给服务端发送消息,如果需要服务端给客户端发送消息,需要接着上面的步骤继续:
- 在客户端中创建一个Handler对象,用于处理服务端发过来的消息。
- 创建一个客户端自己的Messenger对象,并封装Handler。
- 将客户端的Messenger对象赋给待发送的Message对象的replyTo字段。
- 在服务端的Handler处理Message时将客户端的Messenger解析出来,并使用客户端的Messenger对象给客户端发送消息。
其实Messenger底层也是AIDL。客户端和服务端通讯,就是普通的AIDL,客户端实例化Stub之后,通过Stub的send方法把消息发到服务端。服务端和客户端通讯:服务端通过解析Message的replyTo,获得客户端的Stub,然后通过send方法发送到客户端。
注意:Service在声明时必须对外开放,即android:exported="true"
。
4 AIDL(基于Binder)
Google Doc:https://developer.android.google.cn/guide/components/aidl
AIDL:Android Interface Definition Language,即Android接口定义语言。可以利用它定义客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口。
注:只有允许不同应用的客户端用 IPC 方式访问服务,并且想要在服务中处理多线程时,才有必要使用 AIDL。 如果不需要执行跨越不同应用的并发 IPC,就应该通过实现一个 Binder创建接口;或者,如果想执行 IPC,但根本不需要处理多线程,则使用 Messenger 类来实现接口。无论如何,在实现 AIDL 之前,需要理解绑定服务。
从某种意义上说AIDL其实是一个模板,因为在使用过程中,实际起作用的并不是AIDL文件,而是据此而生成的一个IInterface的实例代码,AIDL其实是为了避免我们重复编写代码而出现的一个模板。
通过编写aidl文件来设计想要暴露的接口,编译后会自动生成相应的java文件,服务器将接口的具体实现写在Stub中,用IBinder对象传递给客户端,客户端bindService的时候,用asInterface的形式将IBinder还原成接口,再调用其中的方法。
AIDL支持的数据类型
- 基本数据类型(int、long、char、boolean、double、byte、short、float)
- String 和 CharSequence
- List、Map集合:
List/Map中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或声明的可打包类型。可选择将 List/Map 用作“通用”类(例如,List<String>、Map<String,Integer>)。
另一端实际接收的具体类始终是 ArrayList/HashMap,但生成的方法使用的是 List/Map 接口。 - 实现了 Parcelable 接口的对象
- AIDL本身接口也可以在AIDL文件使用
AIDL使用步骤
- 创建 .aidl 文件
此文件定义带有方法签名的编程接口。 - 实现接口
Android SDK 工具基于您的 .aidl 文件,使用 Java 编程语言生成一个接口。此接口具有一个名为 Stub 的内部抽象类,用于扩展 Binder 类并实现 AIDL 接口中的方法。您必须扩展 Stub 类并实现方法。 - 向客户端公开该接口
实现 Service 并重写 onBind() 以返回 Stub 类的实现。
AIDL详情请移步:Android进程通信-AIDL
5 ContentProvider(基于Binder)
Google Doc: https://developer.android.google.cn/guide/topics/providers/content-providers
系统四大组件之一,底层也是基于Binder实现的,是Android跨进程实现数据共享的标准方式。
ContentProvider主要用于在不同的应用程序间实现数据共享,允许一个程序访问另外一个程序中的数据,还能保证数据访问的安全性,可以说天生就是为进程通信而生的。
- 自己实现一个ContentProvider需要实现6个方法,其中onCreate是主线程中回调的,其他方法是运行在Binder之中的。
- 自定义的ContentProvider注册时要提供
authorities
属性,应用需要访问的时候将属性包装成Uri.parse("content://authorities")
。 - 还可以设置
permission
、readPermission
、writePermission
来设置权限。 - ContentProvider有query,delete,insert等方法,看起来貌似是一个数据库管理类,但其实可以用文件、内存数据等等一切来充当数据源,query返回的是一个Cursor,可以自定义继承AbstractCursor的类来实现。
这里不再赘述,详情可参考:https://blog.csdn.net/hzw2017/article/details/81123791
6 Socket(网络)
请移步:网络通信-Socket