文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android10 Binder原理概述深入解析

2022-11-13 18:16

关注

IPC工具介绍

Binder作为Android 众多的IPC通讯手段之一,在Framework的数据传输中起到极为关键的作用。为什么Google需要重新创造Binder这么一个IPC工具,使用linux默认提供的Pipe、Socket、共享内存、信号、消息队列等IPC工具不行吗?

答案是 这些传统的linux IPC工具有一部分android也在使用,只是在某些场合下它们无法满足需求,所以才创造了Binder这么一个工具。 为了更好地向各位读者解释为什么需要Binder,我们先来简单地认识一下linux传统的IPC工具,让大家对它们的优势和劣势有一个更为直观的认识。

Pipe

管道是一种最基本的IPC工具,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。它有如下特质:

framework中有没有使用Pipe进行通讯?答案是有,但是用的很少,相比之下用的更多的是FIFO!!各位读者如果感兴趣的话,可以在源码中搜一下 TransferPipe这个类,在其中可以找到Pipe的痕迹。

Sign

信号是由用户、系统或者进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常。linux系统已经预置了一部分信号标识,它们都有着特殊的含义,部分信号如下所示:

信号只能起到对进程的通知作用,它无法发送复杂的数据类型,不适合用于进程间的数据交换。

信号在整个framework中也扮演了极为重要的角色,各位读者可以通过搜索sigemptysetsigaddset等关键字,在源码中找到它们的身影。

message queue

消息队列,Unix的通信机制之一,可以理解为是一个存放消息(数据)容器。将消息写入消息队列,然后再从消息队列中取消息,一般来说是先进先出的顺序。消息队列本质上是位于内核空间的链表,链表的每个节点都是一条消息。每一条消息都有自己的消息类型,消息类型用整数来表示,而且必须大于 0。每种类型的消息都被对应的链表所维护。

其中数字 1 表示类型为 1 的消息,数字2、3、4 类似。彩色块表示消息数据,它们被挂在对应类型的链表上。

消息队列的缺陷在于:容量受到系统限制;消息队列的发送方与接收方没有强关联性,容易造成发送方往消息队列中存放了消息,没有接收方来取消息或接收方没有及时取消息的问题,消息的及时性无法保障。

目前在Android 10的非内核源码范围内,没有发现使用消息队列。

shared memory

共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。

共享内存利用内存缓冲区直接交换信息,无须复制,快捷、信息量大是其优点。但是共享内存的通信方式是通过将共享的内存缓冲区直接附加到进程的虚拟地址空间中来实现的,因此,这些进程之间的读写操作的同步问题操作系统无法实现。必须由各进程利用其他同步工具解决,开发上手难度较高,容易出错,且存在数据安全隐患。

目前在Android 10的非内核源码范围内,没有发现使用共享内存。

Socket

Socket这个不需要特别介绍了,不管是做C++开发还是Java开发,都会涉及到套接字编程。相对其他的IPC方式,Socket是最适合做一对多这种通讯需求的。它的问题在于数据需要经过两次拷贝,通讯效率相对低下。这个问题在电脑等设备上都不是什么特别大的问题,但考虑到Android搭载的移动设备,尤其是早期的移动设备,这个问题就很致命了。

framework中当然也存在Socket的使用痕迹,比如 system/core/init/init.cpp这个文件中就采用epoll机制,实现init进程与其子进程的通讯。

Android更看重的是效率和一对多通讯的问题,无法采用传统的IPC工具实现,所以只能考虑自己另起炉灶。除此之外,传统的IPC无法获得对方进程的PID\UID,从而无法鉴别对象的身份,从而会使Android系统的安全性无法得到保证(ps:无法获得对方进程的身份指的是Linux默认没有提供获取通讯进程的身份的接口,并不是说采用传统IPC没有办法实现这样的安全管控需求,只是谷歌在综合考虑了上述所有的因素的情况下,在共享内存的基础上做了一套新的解决方案)。

这里给各位读者留个思考题,有兴趣的读者可以自己动手去实验一下:

在一对多通讯的场景下,Binder的传输效率一定会比Socket高吗?(提示:Socket包括BIO、NIO、NIO2、epoll等,请不要局限在BIO的通讯方式)

AIDL

AIDL 是 Android interface definition Language 的英文缩写, 意思Android 接口定义语言,它与Binder有着千丝万缕的联系。

AIDL是谷歌使用Java 编程语言的语法定义的专门服务于Binder IPC通讯的脚本语言,推出的根本原因是为了避免Binder通讯中大量模板代码的书写。AIDL脚本会在编译期间,由Android SDK 工具生成基于该 .aidl 文件的 IBinder 接口,并将其保存到项目的 generated/ 目录中。

我们来看一个简单的aidl文件:

packageackage com.example.commonservice;
// Declare any non-default types here with import statements
interface ITtsService {
   void showTts(in String uid,in int textId,in boolean toPlayTts,in int type);
   boolean isShowing();
}

它生成的java文件如下所示:


package com.example.commonservice;
// Declare any non-default types here with import statements
public interface ITtsService extends android.os.IInterface
{
  
  public static class Default implements com.example.commonservice.ITtsService
  {
    @Override public void showTts(java.lang.String uid, int textId, boolean toPlayTts, int type) throws android.os.RemoteException
    {
    }
    @Override public boolean isShowing() throws android.os.RemoteException
    {
      return false;
    }
    @Override
    public android.os.IBinder asBinder() {
      return null;
    }
  }
  
  public static abstract class Stub extends android.os.Binder implements com.example.commonservice.ITtsService
  {
    private static final java.lang.String DESCRIPTOR = "com.example.commonservice.ITtsService";
    
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    
    public static com.example.commonservice.ITtsService asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.example.commonservice.ITtsService))) {
        return ((com.example.commonservice.ITtsService)iin);
      }
      return new com.example.commonservice.ITtsService.Stub.Proxy(obj);
    }
    @Override public android.os.IBinder asBinder()
    {
      return this;
    }
    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    {
      java.lang.String descriptor = DESCRIPTOR;
      switch (code)
      {
        case INTERFACE_TRANSACTION:
        {
          reply.writeString(descriptor);
          return true;
        }
        case TRANSACTION_showTts:
        {
          data.enforceInterface(descriptor);
          java.lang.String _arg0;
          _arg0 = data.readString();
          int _arg1;
          _arg1 = data.readInt();
          boolean _arg2;
          _arg2 = (0!=data.readInt());
          int _arg3;
          _arg3 = data.readInt();
          this.showTts(_arg0, _arg1, _arg2, _arg3);
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_isShowing:
        {
          data.enforceInterface(descriptor);
          boolean _result = this.isShowing();
          reply.writeNoException();
          reply.writeInt(((_result)?(1):(0)));
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    private static class Proxy implements com.example.commonservice.ITtsService
    {
      private android.os.IBinder mRemote;
      Proxy(android.os.IBinder remote)
      {
        mRemote = remote;
      }
      @Override public android.os.IBinder asBinder()
      {
        return mRemote;
      }
      public java.lang.String getInterfaceDescriptor()
      {
        return DESCRIPTOR;
      }
      @Override public void showTts(java.lang.String uid, int textId, boolean toPlayTts, int type) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeString(uid);
          _data.writeInt(textId);
          _data.writeInt(((toPlayTts)?(1):(0)));
          _data.writeInt(type);
          boolean _status = mRemote.transact(Stub.TRANSACTION_showTts, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().showTts(uid, textId, toPlayTts, type);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override public boolean isShowing() throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        boolean _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_isShowing, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().isShowing();
          }
          _reply.readException();
          _result = (0!=_reply.readInt());
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      public static com.example.commonservice.ITtsService sDefaultImpl;
    }
    static final int TRANSACTION_showTts = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_isShowing = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    public static boolean setDefaultImpl(com.example.commonservice.ITtsService impl) {
      if (Stub.Proxy.sDefaultImpl == null && impl != null) {
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static com.example.commonservice.ITtsService getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }
  public void showTts(java.lang.String uid, int textId, boolean toPlayTts, int type) throws android.os.RemoteException;
  public boolean isShowing() throws android.os.RemoteException;
}

虽然是简短的一个aidl文件,但生成的模板代码却极为复杂,为了整理清楚这段代码的结构,笔者先隐藏其部分内容:

public interface ITtsService extends android.os.IInterface
{
    
    public static class Default implements com.example.commonservice.ITtsService
    {
    }
    
    public static abstract class Stub extends android.os.Binder implements com.example.commonservice.ITtsService
    {
        private static class Proxy implements com.example.commonservice.ITtsService
        {
        }
        static final int TRANSACTION_showTts = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_isShowing = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }
    public void showTts(java.lang.String uid, int textId, boolean toPlayTts, int type) throws android.os.RemoteException;
    public boolean isShowing() throws android.os.RemoteException;
}

可以看到,代码的结构如同套娃一样,一层接一层。各位读者不妨思考一下,如果不这样套娃,把ITtsService里的DefaultStub类移到外面来,这样做可不可以?

答案是,可以的,不过类的命名方式可能要稍微做一下修改,如ITtsService_DefalutITtsService_Stub,以便于引用上的区分。当然,代码的结构不是重点,虽然谷歌的方式可读性会差一点,但开发人员不需要直接和这些源码打交道,也不是不可以接受。

Binder不一定都是跨进程通讯,同样也支持同进程通讯,比如 Activity绑定Service,通过Binder实现数据传输。在同一进程通讯的情况下,Stub类身兼两职,因其implements了ITtsService,它可以作为客户端的调用方;同时,它也是服务端的实现方。而在跨进程通讯的情况下,则由Proxy来担任客户端的调用方。

至于Default这个类的作用,暂时不明,无法找到相关的资料得知为什么谷歌要生成这么一个类。

在此,AIDL的介绍先告一段落,其中更多的细节将放到后续的文章中再做补充。

HIDL

HIDL的生命周期及其短暂,它从Android 8引入,然后在Android 10 立马被 Stable AIDL 所取代,虽然没啥存在感,但还是简单地提及一下吧。

HAL 接口定义语言(简称 HIDL,发音为“hide-l”)是用于指定 HAL 和其用户之间的接口的一种接口描述语言 (IDL)。HIDL 允许指定类型和方法调用(会汇集到接口和软件包中)。从更广泛的意义上来说,HIDL 是指用于在可以独立编译的代码库之间进行通信的系统。

HIDL 旨在用于进程间通信 (IPC)。进程之间的通信采用 Binder 机制。对于必须与进程相关联的代码库,还可以使用直通模式(在 Java 中不受支持)。

更多HIDL相关的资料,可以参考 source.android.google.cn/docs/core/a…

以上就是Android10 Binder原理概述深入解析的详细内容,更多关于Android10 Binder原理的资料请关注编程网其它相关文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯