• Android学习之路(20) 进程间通信


    IPC

    IPC为 (Inter-Process Communication) 缩写,称为进程间通信或跨进程通信,指两个进程间进行数据交换的过程。安卓中主要采用 Binder 进行进程间通信,当然也支持其他 IPC 方式,如:管道,Socket,文件共享,信号量等。

    Binder简介

    1.为什么使用Binder?

    性能方面:

    在移动设备上(性能受限制的设备,比如要省电),广泛地使用跨进程通信对通信机制的性能有严格的要求,Binder相对于传统的Socket方式,更加高效。Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,共享内存方式一次内存拷贝都不需要,但实现方式又比较复杂。

    Binder传输数据的过程:一次拷贝

    Socket传输数据的过程:两次拷贝

    安全方面:

    传统的进程通信方式对于通信双方的身份并没有做出严格的验证,比如 Socket 通信的IP地址是客户端手动填入,很容易进行伪造。然而,Binder机制从协议本身就支持对通信双方做身份校检,从而大大提升了安全性。

    2.Binder原理

    从进程角度来看IPC(Interprocess Communication)机制

    每个Android的进程,只能运行在自己进程所拥有的虚拟地址空间。例如,对应一个4GB的虚拟地址空间,其中3GB是用户空间,1GB是内核空间。当然内核空间的大小是可以通过参数配置调整的。对于用户空间,不同进程之间是不能共享的,而内核空间却是可共享的。Client进程向Server进程通信,恰恰是利用进程间可共享的内核内存空间来完成底层通信工作的。Client端与Server端进程往往采用ioctl等方法与内核空间的驱动进行交互。

    Binder通信采用C/S架构,从组件视角来说,包含Client、Server、ServiceManager以及Binder驱动,其中ServiceManager用于管理系统中的各种服务。架构图如下所示:

    Binder通信的四个角色

    • Client进程:使用服务的进程。
    • Server进程:提供服务的进程。
    • ServiceManager进程:

    ServiceManager 的作用是将字符形式的 Binder 名字转化成 Client 中对该 Binder 的引用,使得 Client 能够通过 Binder 名字获得对 Server中Binder 实体的引用。

    • Binder驱动:

    ​驱动负责进程之间 Binder 通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。

    3.Binder间数据传递

    进程间数据传递都是需要序列化的。Serializable 和 Parcelable 接口都可以完成序列化,通过 Intent 和 Binder传输数据时候就需要使用 Serializable 和 Parcelable 。

    区别和联系:

    Serializable 是 Java中提供的接口,序列化和反序列化需要大量的 I/O 操作,开销比较大。而 Parcelable 是安卓中的序列化方式,效率高。Parcelable 主要用在内存序列化上,Serializable 主要用在用于文件,网络中传递。

    Binder

    Binder 是 Android 中的一个类,实现了 IBinder 接口。

    ​ 从 IPC 角度来说

    Binder 是 Android 中的一种跨进程通信方式,可以理解为一种虚拟的物理设备,它的设备驱动为 /dev/binder。

    ​ 从 Framework 的角度:

    Binder 是 ServiceManager 连接各种 Manager(ActivityManager,WindowManager) 和 ManagerService 的桥梁。

    ​ 从应用层的角度

    Binder 是客户端和服务端进行通信的媒介,当 bindService 时,服务端会返回一个包含了服务端业务调用的 Binder 对象,通过这个 Binder 对象,客户端就可以获取服务端提供的服务或者数据,包括普通服务和 AIDL 服务。

    Android中IPC方式

    1.使用 Bundle
    Bundle 实现了 Parcelable 接口,可以通过 Intent 进行传递。当从一个进程启动另一个进程的 Activity 时,可以在 Bundle 中附加参数,这些数据必须是能够被序列化的。

    2.使用文件共享
    A进程往文件中写数据,B进程从文件中读数据。在 Windows上,一个文件如果被加了排斥锁将会导致其他线程无法对其进行访问。而 Andorid 是基于 Linux,是的其支持并发 读/写 可以无限制的进行,但是需要注意并发安全问题。

    SharedPreferences 是 Android中轻量级的存储方案,通过键值对来存储数据,采用 xml 文件来保存键值对。因为 Android 系统对 读/写 有一定的缓存策略,即在内存中会有一份 SharedPreferences 文件的缓存,在多进程下,系统对它的读写变得不可靠。SharedPreferences 的 get 个 put 方法都是经过 synchronized 修饰的。

    3.使用 Messenger
    Messenger 也被翻译为信使,通过它可以在不同的进程间传递 Message 对象,在 Message 中放入我们需要传递的数据。它的底层也是常用 AIDL。

    服务端通过 Handler 创建一个 Messenger,然后在 Servce 的 onBind 方法中返回这个 Messenger 对象的底层的 Binder即可。客户端通过服务端返回的 IBinder 就可以创建 Messenger来服务端发消息了。

    4.使用 AIDL

    AIDL 意思即 Android Interface Definition Language,翻译过来就是Android接口定义语言,是用于定义服务器和客户端通信接口的一种描述语言,可以拿来生成用于IPC的代码。从某种意义上说AIDL其实是一个模板,因为在使用过程中,实际起作用的并不是AIDL文件,而是据此而生成的一个IInterface的实例代码,AIDL其实是为了避免我们重复编写代码而出现的一个模板

    进程间的通信,速度快(系统底层直接是共享内存),性能稳,效率高,一般进程间通信就用它. AIDL是Binder机制向外提供的接口,目的就是为了方便对Binder的使用.

    5.使用 ContentProvider
    ​ ContentProvider 是 Android 提供的专门用于不同应用间进行数据共享的方式,它的底层实现也是 Binder。

    6.使用 Socket
    ​ Socket 也被称为 “套接字”,分为流式套接字和用户数据报套接字两种,分别对应于网络传输控制层中的 TCP 和 UDP协议。

    IPC各种方式比较

    Binder连接池

    在这种模式下,整个工作机制是这样的;每个业务模块创建自己的AIDL接口并实现此接口,这个时候不同业务模块之间是不能有耦合的,所有实现细节我们要单独开来,然后向服务端提供自己的唯一标识和其对应的Binder对象;对于服务端来说,只需要一个Service就可以了,服务端提供一个queryBinder接口,这个接口能够根据业务模块的特征来返回相应的Binder对象给它们,不同的业务模块拿到所需的Binder对象后就可以进行远程方法调用了。由此可见,Binder连接池的主要作用就是将每个业务模块的Binder请求统一转发到远程Service中去执行,从而避免了重复创建Service的过程,它的工作原理如图所示。

    Binder的使用

    定义接口

    Binder机制通常用于实现进程间的接口调用,我们需要先定义Binder接口。一个典型的Binder接口如下:

    public interface IBookManager {
      public void addBook(Book book);
      public void deleteBook(int bookId);
      public Book getBook(int bookId);
      public List getBooks();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这是一个书管理的Binder接口,包含增加书籍、删除书籍、获取书籍等方法。

    实现Binder接口

    然后我们需要实现上面的Binder接口,并继承自Binder类,这样这个类的实例就代表了一个Binder实体,可以跨进程传输。

    public class BookManager extends Binder implements IBookManager {
      public void addBook(Book book) { ... }
      public void deleteBook(int bookId) { ... }
      public Book getBook(int bookId) { ... }
      public List getBooks() { ... }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在Server端注册Binder实体

    在提供服务的进程内,我们需要将Binder实体注册到ServiceManager中。这样当其他进程想访问这个Binder实体时,就可以从ServiceManager中获取它。

    public class BookManagerService extends Service {
      private final BookManager bookManager = new BookManager();
      
      public void onCreate() {
        super.onCreate();
        publishBinderService(CMD_BOOK_MANAGER, bookManager);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在onCreate()方法中我们通过publishBinderService()方法将bookManagerBinder实体发布到ServiceManager中,这样其他进程就可以通过CMD_BOOK_MANAGER这个key来获取这个Binder实体了。

    在Client端获取Binder代理并调用

    在客户端进程中,我们可以通过ServiceManager访问Server发布的Binder实体。

    public void queryBookManager() {
      // 获取ServiceManager的代理
      IBinder service = ServiceManager.getService(CMD_BOOK_MANAGER);
      
      // 通过服务端返回的IBinder对象生成Binder代理
      IBookManager bookManager = IBookManager.Stub.asInterface(service);
      
      // 通过代理调用服务端的方法
      List books = bookManager.getBooks();
      ... 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    我们首先通过ServiceManager获取要访问的Binder实体,这个会返回一个IBinder对象。然后通过IBookManager.Stub.asInterface(service)将IBinder对象转成客户端所需的IBookManager接口类型,这个对象就是Binder代理。我们可以通过这个代理对象调用服务端IBookManager接口中的方法。

    至此,我们完成了一个跨进程的Binder通信的全过程。客户端通过Binder代理访问服务器提供的服务,服务器通过Binder实体来处理客户端的请求。这整套机制都建立在Binder驱动之上,Binder驱动负责将双方的请求和回复进行传递。

  • 相关阅读:
    CentOS7安装xvfb,解决服务器没有X-Server的问题
    简要介绍django框架
    SDL音视频渲染
    nginx配置指南
    【技术分享】一文2500字实战详解接口测试请求方式Get、post
    prometheus Histogram 统计原理
    基于Http Basic Authentication的接口
    Java校园跑腿小程序校园代买帮忙外卖源码社区外卖源码
    【openGauss】一主一备实现主备节点切换实验(switchover、failover)
    C++ Reference: Standard C++ Library reference: Containers: deque: deque: rbegin
  • 原文地址:https://blog.csdn.net/qq_32907491/article/details/133562948