Android进程通信

参考:https://blog.csdn.net/hzw2017/article/details/81275438

一、进程

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进程通信_第1张图片
Android进程层次.png

RPC 和 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 来定义接口。
使用步骤
服务端:
  1. 创建一个Handler对象,并实现handleMessage方法,用于接收和处理来自客户端的消息。
  2. 创建一个Messenger作为送信人,封装Handler。
  3. 用Messenger的getBinder()方法获取一个IBinder对象,通过onBind返回给客户端。
客户端:
  1. 在Activity中绑定服务。
  2. 创建ServiceConnection并在其中使用 IBinder 将 Messenger实例化。
  3. 使用Messenger向服务端发送消息。
  4. 解绑服务。
  5. 服务端中在 handleMessage() 方法中接收每个 Message。

以上实现的仅仅是单向通信,即客户端给服务端发送消息,如果需要服务端给客户端发送消息,需要接着上面的步骤继续:

  1. 在客户端中创建一个Handler对象,用于处理服务端发过来的消息。
  2. 创建一个客户端自己的Messenger对象,并封装Handler。
  3. 将客户端的Messenger对象赋给待发送的Message对象的replyTo字段。
  4. 在服务端的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、Map)。
    另一端实际接收的具体类始终是 ArrayList/HashMap,但生成的方法使用的是 List/Map 接口。
  • 实现了 Parcelable 接口的对象
  • AIDL本身接口也可以在AIDL文件使用

AIDL使用步骤

  1. 创建 .aidl 文件
    此文件定义带有方法签名的编程接口。
  2. 实现接口
    Android SDK 工具基于您的 .aidl 文件,使用 Java 编程语言生成一个接口。此接口具有一个名为 Stub 的内部抽象类,用于扩展 Binder 类并实现 AIDL 接口中的方法。您必须扩展 Stub 类并实现方法。
  3. 向客户端公开该接口
    实现 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")
  • 还可以设置permissionreadPermissionwritePermission来设置权限。
  • ContentProvider有query,delete,insert等方法,看起来貌似是一个数据库管理类,但其实可以用文件、内存数据等等一切来充当数据源,query返回的是一个Cursor,可以自定义继承AbstractCursor的类来实现。

这里不再赘述,详情可参考:https://blog.csdn.net/hzw2017/article/details/81123791

6 Socket(网络)

请移步:网络通信-Socket

6种IPC方式对比

Android进程通信_第2张图片
IPC方式对比.jpg

你可能感兴趣的:(Android进程通信)