精华内容
下载资源
问答
  • Android跨进程通信:图文详解 Binder机制 原理

    万次阅读 多人点赞 2017-06-22 10:31:24
    如果你接触过 跨进程通信 (IPC),那么你对Binder一定不陌生 虽然 网上有很多介绍 Binder的文章,可是存在一些问题:浅显的讨论Binder机制 或 一味讲解 Binder源码、逻辑不清楚,最终导致的是读者们还是无法形成一...

    前言

    • 如果你接触过 跨进程通信 (IPC),那么你对Binder一定不陌生
    • 虽然 网上有很多介绍 Binder的文章,可是存在一些问题:浅显的讨论Binder机制 或 一味讲解 Binder源码、逻辑不清楚,最终导致的是读者们还是无法形成一个完整的Binder概念
    • 本文采用 清晰的图文讲解方式,按照 大角度 -> 小角度 去分析Binder,即:
      1. 先从 机制、模型的角度 去分析 整个Binder跨进程通信机制的模型
      2. 再 从源码实现角度,分析 BinderAndroid中的具体实现

    从而全方位地介绍 Binder,希望你们会喜欢。

    请尽量在PC端而不要在移动端看,否则图片可能看不清。


    目录

    目录


    1. Binder到底是什么?

    • 中文即 粘合剂,意思为粘合了两个不同的进程

    • 网上有很多对Binder的定义,但都说不清楚:Binder是跨进程通信方式、它实现了IBinder接口,是连接 ServiceManager的桥梁blabla,估计大家都看晕了,没法很好的理解

    • 我认为:对于Binder的定义,在不同场景下其定义不同

    定义

    在本文的讲解中,按照 大角度 -> 小角度 去分析Binder,即:

    • 先从 机制、模型的角度 去分析 整个Binder跨进程通信机制的模型

    其中,会详细分析模型组成中的 Binder驱动

    • 再 从源码实现角度,分析 BinderAndroid中的具体实现

    从而全方位地介绍 Binder,希望你们会喜欢。


    2. 知识储备

    在讲解Binder前,我们先了解一些Linux的基础知识

    2.1 进程空间划分

    • 一个进程空间分为 用户空间 & 内核空间(Kernel),即把进程内 用户 & 内核 隔离开来
    • 二者区别:
      1. 进程间,用户空间的数据不可共享,所以用户空间 = 不可共享空间
      2. 进程间,内核空间的数据可共享,所以内核空间 = 可共享空间

    所有进程共用1个内核空间

    • 进程内 用户空间 & 内核空间 进行交互 需通过 系统调用,主要通过函数:
    1. copy_from_user():将用户空间的数据拷贝到内核空间
    2. copy_to_user():将内核空间的数据拷贝到用户空间

    示意图

    2.2 进程隔离 & 跨进程通信( IPC )

    • 进程隔离
      为了保证 安全性 & 独立性,一个进程 不能直接操作或者访问另一个进程,即Android的进程是相互独立、隔离的

    • 跨进程通信( IPC
      即进程间需进行数据交互、通信

    • 跨进程通信的基本原理

    示意图

    a. 而Binder的作用则是:连接 两个进程,实现了mmap()系统调用,主要负责 创建数据接收的缓存空间 & 管理数据接收缓存
    b. 注:传统的跨进程通信需拷贝数据2次,但Binder机制只需1次,主要是使用到了内存映射,具体下面会详细说明

    2.5 内存映射

    具体请看文章:操作系统:图文详解 内存映射


    3. Binder 跨进程通信机制 模型

    3.1 模型原理图

    Binder 跨进程通信机制 模型 基于 Client - Server 模式
    示意图

    3.2 模型组成角色说明

    示意图

    此处重点讲解 Binder驱动的作用 & 原理:

    • 简介

    示意图

    • 跨进程通信的核心原理

    关于其核心原理:内存映射,具体请看文章:操作系统:图文详解 内存映射

    示意图

    3.3 模型原理步骤说明

    示意图

    3.4 额外说明

    说明1:Client进程、Server进程 & Service Manager 进程之间的交互 都必须通过Binder驱动(使用 openioctl文件操作函数),而非直接交互

    原因:

    1. Client进程、Server进程 & Service Manager进程属于进程空间的用户空间,不可进行进程间交互
    2. Binder驱动 属于 进程空间的 内核空间,可进行进程间 & 进程内交互

    所以,原理图可表示为以下:

    虚线表示并非直接交互

    示意图

    说明2: Binder驱动 & Service Manager进程 属于 Android基础架构(即系统已经实现好了);而Client 进程 和 Server 进程 属于Android应用层(需要开发者自己实现)

    所以,在进行跨进程通信时,开发者只需自定义Client & Server 进程 并 显式使用上述3个步骤,最终借助 Android的基本架构功能就可完成进程间通信

    示意图

    说明3:Binder请求的线程管理
    • Server进程会创建很多线程来处理Binder请求
    • Binder模型的线程管理 采用Binder驱动的线程池,并由Binder驱动自身进行管理

    而不是由Server进程来管理的

    • 一个进程的Binder线程数默认最大是16,超过的请求会被阻塞等待空闲的Binder线程。

    所以,在进程间通信时处理并发问题时,如使用ContentProvider时,它的CRUD(创建、检索、更新和删除)方法只能同时有16个线程同时工作


    • 至此,我相信大家对Binder 跨进程通信机制 模型 已经有了一个非常清晰的定性认识
    • 下面,我将通过一个实例,分析Binder跨进程通信机制 模型在 Android中的具体代码实现方式

    即分析 上述步骤在Android中具体是用代码如何实现的


    4. Binder机制 在Android中的具体实现原理

    • Binder机制在 Android中的实现主要依靠 Binder类,其实现了IBinder 接口

    下面会详细说明

    • 实例说明:Client进程 需要调用 Server进程的加法函数(将整数a和b相加)

    即:

    1. Client进程 需要传两个整数给 Server进程
    2. Server进程 需要把相加后的结果 返回给Client进程
    • 具体步骤
      下面,我会根据Binder 跨进程通信机制 模型的步骤进行分析

    步骤1:注册服务

    • 过程描述
      Server进程 通过Binder驱动 向 Service Manager进程 注册服务
    • 代码实现
      Server进程 创建 一个 Binder 对象
    1. Binder 实体是 Server进程 在 Binder 驱动中的存在形式
    2. 该对象保存 ServerServiceManager 的信息(保存在内核空间中)
    3. Binder 驱动通过 内核空间的Binder 实体 找到用户空间的Server对象
    • 代码分析
        
        Binder binder = new Stub();
        // 步骤1:创建Binder对象 ->>分析1
    
        // 步骤2:创建 IInterface 接口类 的匿名类
        // 创建前,需要预先定义 继承了IInterface 接口的接口 -->分析3
        IInterface plus = new IPlus(){
    
              // 确定Client进程需要调用的方法
              public int add(int a,int b) {
                   return a+b;
             }
    
              // 实现IInterface接口中唯一的方法
              public IBinder asBinder(){ 
                    return null ;
               }
    };
              // 步骤3
              binder.attachInterface(plus,"add two int");
             // 1. 将(add two int,plus)作为(key,value)对存入到Binder对象中的一个Map<String,IInterface>对象中
             // 2. 之后,Binder对象 可根据add two int通过queryLocalIInterface()获得对应IInterface对象(即plus)的引用,可依靠该引用完成对请求方法的调用
            // 分析完毕,跳出
    
    
    <-- 分析1:Stub类 -->
        public class Stub extends Binder {
        // 继承自Binder类 ->>分析2
    
              // 复写onTransact()
              @Override
              boolean onTransact(int code, Parcel data, Parcel reply, int flags){
              // 具体逻辑等到步骤3再具体讲解,此处先跳过
              switch (code) { 
                    case Stub.add: { 
    
                           data.enforceInterface("add two int"); 
    
                           int  arg0  = data.readInt();
                           int  arg1  = data.readInt();
    
                           int  result = this.queryLocalIInterface("add two int") .add( arg0,  arg1); 
    
                            reply.writeInt(result); 
    
                            return true; 
                      }
               } 
          return super.onTransact(code, data, reply, flags); 
    
    }
    // 回到上面的步骤1,继续看步骤2
    
    <-- 分析2:Binder 类 -->
     public class Binder implement IBinder{
        // Binder机制在Android中的实现主要依靠的是Binder类,其实现了IBinder接口
        // IBinder接口:定义了远程操作对象的基本接口,代表了一种跨进程传输的能力
        // 系统会为每个实现了IBinder接口的对象提供跨进程传输能力
        // 即Binder类对象具备了跨进程传输的能力
    
            void attachInterface(IInterface plus, String descriptor);
            // 作用:
              // 1. 将(descriptor,plus)作为(key,value)对存入到Binder对象中的一个Map<String,IInterface>对象中
              // 2. 之后,Binder对象 可根据descriptor通过queryLocalIInterface()获得对应IInterface对象(即plus)的引用,可依靠该引用完成对请求方法的调用
    
            IInterface queryLocalInterface(Stringdescriptor) ;
            // 作用:根据 参数 descriptor 查找相应的IInterface对象(即plus引用)
    
            boolean onTransact(int code, Parcel data, Parcel reply, int flags);
            // 定义:继承自IBinder接口的
            // 作用:执行Client进程所请求的目标方法(子类需要复写)
            // 参数说明:
            // code:Client进程请求方法标识符。即Server进程根据该标识确定所请求的目标方法
            // data:目标方法的参数。(Client进程传进来的,此处就是整数a和b)
            // reply:目标方法执行后的结果(返回给Client进程)
             // 注:运行在Server进程的Binder线程池中;当Client进程发起远程请求时,远程请求会要求系统底层执行回调该方法
    
            final class BinderProxy implements IBinder {
             // 即Server进程创建的Binder对象的代理对象类
             // 该类属于Binder的内部类
            }
            // 回到分析1原处
    }
    
    <-- 分析3:IInterface接口实现类 -->
    
     public interface IPlus extends IInterface {
              // 继承自IInterface接口->>分析4
              // 定义需要实现的接口方法,即Client进程需要调用的方法
             public int add(int a,int b);
    // 返回步骤2
    }
    
    <-- 分析4:IInterface接口类 -->
    // 进程间通信定义的通用接口
    // 通过定义接口,然后再服务端实现接口、客户端调用接口,就可实现跨进程通信。
    public interface IInterface
    {
        // 只有一个方法:返回当前接口关联的 Binder 对象。
        public IBinder asBinder();
    }
      // 回到分析3原处
    

    注册服务后,Binder驱动持有 Server进程创建的Binder实体

    步骤2:获取服务

    • Client进程 使用 某个 service前(此处是 相加函数),须 通过Binder驱动 向 ServiceManager进程 获取相应的Service信息
    • 具体代码实现过程如下:

    示意图

    此时,Client进程与 Server进程已经建立了连接

    步骤3:使用服务

    Client进程 根据获取到的 Service信息(Binder代理对象),通过Binder驱动 建立与 该Service所在Server进程通信的链路,并开始使用服务

    • 过程描述

      1. Client进程 将参数(整数a和b)发送到Server进程
      2. Server进程 根据Client进程要求调用 目标方法(即加法函数)
      3. Server进程 将目标方法的结果(即加法后的结果)返回给Client进程
    • 代码实现过程

    步骤1: Client进程 将参数(整数a和b)发送到Server进程

    // 1. Client进程 将需要传送的数据写入到Parcel对象中
    // data = 数据 = 目标方法的参数(Client进程传进来的,此处就是整数a和b) + IInterface接口对象的标识符descriptor
      android.os.Parcel data = android.os.Parcel.obtain();
      data.writeInt(a); 
      data.writeInt(b); 
    
      data.writeInterfaceToken("add two int");;
      // 方法对象标识符让Server进程在Binder对象中根据"add two int"通过queryLocalIInterface()查找相应的IInterface对象(即Server创建的plus),Client进程需要调用的相加方法就在该对象中
    
      android.os.Parcel reply = android.os.Parcel.obtain();
      // reply:目标方法执行后的结果(此处是相加后的结果)
    
    // 2. 通过 调用代理对象的transact() 将 上述数据发送到Binder驱动
      binderproxy.transact(Stub.add, data, reply, 0)
      // 参数说明:
        // 1. Stub.add:目标方法的标识符(Client进程 和 Server进程 自身约定,可为任意)
        // 2. data :上述的Parcel对象
        // 3. reply:返回结果
        // 0:可不管
    
    // 注:在发送数据后,Client进程的该线程会暂时被挂起
    // 所以,若Server进程执行的耗时操作,请不要使用主线程,以防止ANR
    
    
    // 3. Binder驱动根据 代理对象 找到对应的真身Binder对象所在的Server 进程(系统自动执行)
    // 4. Binder驱动把 数据 发送到Server 进程中,并通知Server 进程执行解包(系统自动执行)
    
    

    步骤2:Server进程根据Client进要求 调用 目标方法(即加法函数)

    // 1. 收到Binder驱动通知后,Server 进程通过回调Binder对象onTransact()进行数据解包 & 调用目标方法
      public class Stub extends Binder {
    
              // 复写onTransact()
              @Override
              boolean onTransact(int code, Parcel data, Parcel reply, int flags){
              // code即在transact()中约定的目标方法的标识符
    
              switch (code) { 
                    case Stub.add: { 
                      // a. 解包Parcel中的数据
                           data.enforceInterface("add two int"); 
                            // a1. 解析目标方法对象的标识符
    
                           int  arg0  = data.readInt();
                           int  arg1  = data.readInt();
                           // a2. 获得目标方法的参数
                          
                           // b. 根据"add two int"通过queryLocalIInterface()获取相应的IInterface对象(即Server创建的plus)的引用,通过该对象引用调用方法
                           int  result = this.queryLocalIInterface("add two int") .add( arg0,  arg1); 
                          
                            // c. 将计算结果写入到reply
                            reply.writeInt(result); 
                            
                            return true; 
                      }
               } 
          return super.onTransact(code, data, reply, flags); 
          // 2. 将结算结果返回 到Binder驱动
    
    
    

    步骤3:Server进程 将目标方法的结果(即加法后的结果)返回给Client进程

      // 1. Binder驱动根据 代理对象 沿原路 将结果返回 并通知Client进程获取返回结果
      // 2. 通过代理对象 接收结果(之前被挂起的线程被唤醒)
    
        binderproxy.transact(Stub.ADD, data, reply, 0);
        reply.readException();;
        result = reply.readInt();
              }
    }
    
    • 总结
      下面,我用一个原理图 & 流程图来总结步骤3的内容

    原理图

    流程图


    5. 优点

    对比 LinuxAndroid基于Linux)上的其他进程通信方式(管道、消息队列、共享内存、
    信号量、Socket),Binder 机制的优点有:
    示意图


    6. 总结

    • 本文主要详细讲解 跨进程通信模型 Binder机制 ,总结如下:

    定义

    特别地,对于从模型结构组成的Binder驱动来说:

    示意图

    • 整个Binder模型的原理步骤 & 源码分析

    示意图

    Carson带你学Android 文章系列:
    Carson带你学Android:页面活动-Activity
    Carson带你学Android:广播-BroadcastReceiver
    Carson带你学Android:服务-Service
    Carson带你学Android:内存承载器-ContentProvider


    欢迎关注Carson_Ho的CSDN博客 与 公众号!

    博客链接:https://carsonho.blog.csdn.net/


    请帮顶 / 评论点赞!因为你的鼓励是我写作的最大动力!

    展开全文
  • AIDL 跨进程通信

    2018-04-17 11:13:40
    AIDL 跨进程通信,内含服务端和客户端,收发基本数据类型、对象类型
  • 跨进程通信基础篇 含义: 跨进程通信,是指两个进程之间数据交换的过程,英文全称 Inter-Process Communication 缩写IPC 进程和线程的关系: 在移动设备上,一个程序或者一个应用,就是一个进程,一个进程里面可以包含...

    跨进程通信基础篇

    含义:
    跨进程通信,是指两个进程之间数据交换的过程,英文全称 Inter-Process Communication 缩写IPC

    进程和线程的关系:
    在移动设备上,一个程序或者一个应用,就是一个进程,一个进程里面可以包含多个线程
    (例如:一个应用里面有一个主线程(用于更新UI)和多个子线程(执行耗时的任务))

    开启多线程的方法:
    在Android中开启多进程只有一种方法: 那就是给4大组件(Activity,Service,Receiver,ContentProvider)在Menifest中指定android:process属性,除此之外没有其他的方法(通过JNI在native层去fork一个新进程除外)

    实例1如下:

    <activity
        android:name=".activity.Main2Activity"
        android:process=":remote1" />
    

    实例2如下:

    <activity
        android:name=".activity.Main3Activity"
        android:process="com.hlj.demo.remote2" />
    

    说明:
    1.首先实例1中 ":“的含义是指:这是一种简写
    a.当前的进程名称前面要附加上当前的包名 进程完整名称:com.hlj.demo:remote1
    b.以”:"开头的进程属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中
    2,实例2中的进程名称是一种完整的命名方式,不会附加包名信息,其次它属于全局进程,其他应用通过ShareUID方式可以和它跑在同一个进程中.(2个应用的签名也要相同)

    注意:
    程序入口MainActivity,默认没有给它process属性,那么它就运行在默认的进程中,默认的进程名称是包名,如果给它指定了process属性,那么它就运行在该指定进程当中

    如何查看自己开启了多少个进程呢,studio如下图所示:
    在这里插入图片描述
    运行在同一个进程中的组件是属于同一个虚拟机和同一个Application,可以共享Applicaiton下的数据,运行在不同进程中的组件是属于2个不同的虚拟机和2个不同的Application,2个不同的Application数据是不能互通的

    IPC的基础概念: Serializable接口,Parcelable接口,Binder

    Serializable接口:Serializable是java所提供的一个序列化的接口,它是一个空接口,为对象提供序列化和反序列化操作

    //实现Serializable接口即可
    public class UserBean implements Serializable{
        private static final long serialVersionUID=1L;
        public String name;
    }
    
    UserBean user = new UserBean();
    user.name = "hao123";
    if (!StrUtils.IsKong(Environment.getExternalStorageDirectory() + "/" + "demo")) {
          String baseExternalStoragepath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + "demo";
          baseDirectory = new File(baseExternalStoragepath);
          if (!baseDirectory.exists()) {
              //先创建文件夹
              baseDirectory.mkdirs();
          }
          //再创建文件
          File wejian = new File(baseDirectory.getPath() + "/" + "user.txt");
          if (!wejian.exists()) {
              wejian.createNewFile();
          }
          //序列化到文件中
          ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(wejian));
          out.writeObject(user);
          out.flush();
          out.close();   
      }
    
    
     //反序列化,从文件中读取文件
      File wejian=new File(baseDirectory.getPath()+"/"+"user.txt");
      ObjectInputStream in=new ObjectInputStream(new FileInputStream(wejian));
      UserBean user=(UserBean)in.readObject();
      in.close();
    

    注意:
    序列化和反序列化需要读写文件,所以需要在清单文件配置读写权限,大于6.0的设备还要动态申请权限

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    

    serialVersionUID:
    一般来说我们应该手动指定serialVersionUID的值,这样做的好处是:当版本升级后,我们可能删除了某个成员变量也可能增加了一些新的成员变量,但指定了这个值以后,反序列化还是可以成功,程序可以最大限度的恢复数据,如果不指定serialVersionUID的值的话,程序会挂掉,但是如果类结构发生了非常规性的改变,例如修改了成员变量的类型结构,这种毁灭性的改变后,反序列化还是会失败,因为无法从老版本的数据中还原一个新的类型结构的对象

    注意:
    1,静态成员属于类不属于对象,所以不会参与序列化的过程
    2,用transient关键字标记的成员变量不会参与序列化的过程

    Parcelable接口
    Parcelable 是android中提供的新的序列化方式,只要实现这个接口,一个类的对象就可以实现序列化,并且可以通过Intent和Binder传输

    实例如下:

    public class User2 implements Parcelable {
        public  int age;
        //体重
        public  int weight;
        public  String name;
        public Book book;
    
        public User2(){
        
        }
    
        public User2(Parcel in) {
        //注意:这里读取字段的属性要和写入字段的顺序一样 age,weight,name,book
            age=in.readInt();
            weight=in.readInt();
            name=in.readString();
            //由于book是另一个序列化对象,所以它的反序列化的过程要传递当前上下文类加载器
            book= in.readParcelable(Thread.currentThread().getContextClassLoader());
            
        }
    
        //studio会自动生成这里
        public static final Creator<User2> CREATOR = new Creator<User2>() {
            @Override
            public User2 createFromParcel(Parcel in) {
                return new User2(in);
            }
    
            @Override
            public User2[] newArray(int size) {
                return new User2[size];
            }
        };
    
        //studio会自动生成这里
        @Override
        public int describeContents() {
            return 0;
        }
    
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(age);
            dest.writeInt(weight);
            dest.writeString(name);
            dest.writeParcelable(book,0);
        }
    }
    
    

    说明:
    1,从Parcel in里面读取字段的顺序一定要和writeToParcel字段的顺序一样(否则同一个类型的字段例如age和weight数据会搞混)

    2,由于Book是另一个序列化对象,所以它的反序列化的过程要传递当前上下文类加载器

    Parcelable接口和Serializable接口的比较
    共同点:都能实现序列化并且都可用于Intent间的数据传递
    不同点:Serializable是java中提供的接口,使用起来更加简单,但是开销很大,需要大量的IO操作,Parcelable是android中的序列化方式,所以更适用与android平台,缺点是使用起来稍微麻烦一点,优点是效率高

    Binder:
    直观来讲,Binder是android中的一个类,它实现了IBinder接口.从IPC的角度来讲,Binder是Android中的一种跨进程通信方式,从AndroidFramework角度来讲,Binder是ServiceManager连接各种Manager(ActivityNamager,WindowManger等等)的桥梁;从Android应用层来讲,Binder是客户端和服务端进行通信的媒介,当binderService的时候,服务端会返回一个包含服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包含普通的服务和基于AIDL的服务,普通的service服务不涉及进程间通信,主要是AIDL会涉及binder的核心

    结尾:
    下一篇会总结跨进程通信的几种方式,尽情期待啦~^^

    展开全文
  • 跨进程通信AIDL

    2018-06-12 16:30:37
    简单实现aidl的跨进程通信,快速了解AIDL接口定义语言的使用
  • android aidl跨进程通信

    2015-08-28 17:33:05
    android aidl跨进程通信例子,简单明了
  • 主要介绍了WinForm实现跨进程通信的方法,通过一个WinMessageHelper类实现这一功能,需要的朋友可以参考下
  • 写作原因:跨进程通信的实现和理解是Android进阶中重要的一环。下面博主分享IPC一些相关知识、操作及自己在学习IPC过程中的一些理解。这一章使用Messenger实现跨进程通信,其中bindService基础部分参见Android IPC...
  • 跨进程通信.pptx

    2019-05-11 00:24:38
    跨进程通信.pptx 自己写的PPT 用于演讲
  • Android aidl跨进程通信

    2017-06-08 14:14:17
    Android aidl跨进程通信
  • 本篇文章主要介绍了Android AIDL实现跨进程通信的示例代码,具有一定的参考价值,有兴趣的可以了解一下
  • Android AIDL跨进程通信

    2017-09-30 15:04:21
    Android AIDL跨进程通信 的demo,包括两部分,两个项目
  • 主要介绍了Android编程实现AIDL(跨进程通信)的方法,结合实例形式详细分析了Android实现AIDL(跨进程通信)的原理、具体流程与相关实现技巧,需要的朋友可以参考下
  • 跨进程通信SendMessage - C#, WM_SETTEXT, WM_GETTEXT
  • Android的跨进程通信

    2017-03-03 10:48:16
    Android系统的跨进程简介为什么不能直接跨进程通信?为了安全考虑,应用之间的内存是无法互相访问的,各自的数据都存在于自身的内存区域内。如何跨进程通信?要想跨进程通信,就要找到一个大家都能访问的地方,例如...

    Android系统的跨进程简介

    为什么不能直接跨进程通信?

    为了安全考虑,应用之间的内存是无法互相访问的,各自的数据都存在于自身的内存区域内。

    如何跨进程通信?

    要想跨进程通信,就要找到一个大家都能访问的地方,例如硬盘上的文件,多个进程都可以读写该文件,通过对该文件进行读写约定好的数据,来达到通信的目的。

    Android中的跨进程通信采用的是Binder机制,其底层原理是共享内存。

    Binder机制

    • Android中的跨进程通信采用的是Binder机制。
    • Binder在linux层面属于一个驱动,但是这个驱动不是去驱动一个硬件,而且驱动一小段内存。
    • 不同的应用通过对一块内存区域进行数据的读、写操作来达到通信的目的。
    • 不同的应用在同一内存区域读写数据,为了告知其他应用如何理解写入的数据,就需要一个说明文件,这就是AIDL。当两个应用持有相同的AIDL文件,就能互相理解对方的的意图,就能做出相应的回应,达到通信的目的。

    系统服务

    什么是系统服务?

    由android系统提供的服务,以供应用程序调用,来操作手机。如果应用能直接操作手机,后果将不堪设想,为了安全性和统一性,所有手机操作都将由系统来完成,应用通过发送消息给系统服务来请求操作。系统服务就是系统开放给应用端的操作接口,类似于Web服务开放出来的接口。

    我们通过context. getSystemService可以获取到系统服务的代理对象,该代理对象内部有一个系统服务的远程对象引用。代理对象和系统服务有相同的api接口,我们调用代理对象,代理对象会调用远程对象,远程对象通知系统服务,这样操作起来就像直接访问系统服务一样轻松。

    系统服务和应用端的通信机制

    系统服务和应用端的通信机制

    • 系统服务XxxService

    是一个Binder类的子类,一旦创建后,就开启一个线程死循环用来检测某段内存是否有数据写入

    • Binder驱动

    自身创建时,创建一个XxxRemote远程对象,存放到Binder驱动中,XxxRemote远程对象可以和XxxService系统服务通信

    • 应用端

    通过context. getSystemService()获取XxxServiceProxy对象,该对象内部引用了XxxRemote对象, XxxServiceProxy和XxxService具有相同的API,我们调用XxxServiceProxy时,XxxServiceProxy就调用XxxRemote并等待XxxRemote返回。XxxRemote会往某段内存中写入数据,写完后就开始监视该内存区域,Binder驱动会把XxxRemote写入的数据拷贝到XxxService监视着的内存区域,当XxxService一旦发现有数据,就读取并进行处理,处理完毕后,就写入该区域,这是Binder驱动又会把该数据拷贝到XxxRemote监视的内存区域,当XxxService发现内存区域有数据读取该区域数据,并把内容返回给XxxServiceProxy。这样就完成了一次进程间的通信。

    所以一个系统服务会产生两个Binder对象,一个是运行在系统中的系统服务本身,一个是存放到Binder驱动中的远程对象。所不同的是系统服务Binder对象对开启一个线程监听消息,远程对象不会,它是运行在调用者的线程中。

    客户端也可以不使用系统服务的远程Binder对象,而是自己创建一个Binder对象,通过Binder驱动和系统服务进行关联,这样的好处客户端可以随时通知系统服务,系统服务也可以随时通知客户端,而不是像上面所说的系统服务只能被动的等着客户端调用。

    Binder间的通信机制

    Binder对象都有各自的内存区域,当Binder1想要向Binder2发送数据时,就会把数据写入自己的内存区域,然后通知Binder驱动,Binder驱动会把数据拷贝到Binder2的内存区域,然后通知Binder2进行读取,Binder读取完毕后,将把数据写入binder2的内存区域,然后通知Binder驱动,Binder驱动将会把数据拷贝到Binder1的内存区域中。这样就完成了一次通信。

    如果Binder1是系统服务,Binder2是系统服务的远程对象,这样任何应用程序在获取了Binder2的引用后,都可以和Binder1进行通信。但是缺点也很明显,只能由应用端请求系统服务,系统服务不能主动去联系应用端。WifiManagerService之类的就是采用这种方式。

    还有一种方式是Binder1是系统服务,Binder2是应用端创建的Binder对象,他们两者通过Binder驱动进行连接后,应用端可以主动调系统服务,系统服务也可以主动调用应用端。WindowManagerService就是采用的这种方式。

    IPC

    进程间通信或跨进程通信,是指两个进程间进行数据交换的过程。

    PRC:远程过程调用

    多进程

    线程是CPU调度的最小单元,同时线程是一种有限的系统资源。而进程一般指一个执行单元,在PC和移动设备上指一个程序或者一个应用。一个进程可以包含多个线程,因此进程和线程是包含与被包含的关系。

    • Intent
    • Bundle
    • 共享文件
    • SharedPrefrence
    • ContentProvider
    • Messenger
    • AIDL
    • Binder
    • Socket

    进程间通信

    Android中的多进程模式

    • 一个应用中存在多个进程的情况
    • 多个应用间的多进程
    android:process=":remote" 私有进程
    android:process="com.google.googleplay.remote" 全局进程

    序列化

    序列化,反序列化,持久化

    Serializable

    Java提供的一个序列化接口

    序列化流

    • ObjectInputStream
    • ObjectOutputStream

    Parcelable

    Android提供的序列化接口

    Binder

    实现IBinder接口,是Android的一种跨进程通信方式,是客户端和服务端进行通信的媒介

    • IBinder
    • Binder
    • Stub

    当客户端发起远程请求时,由于当前线程会被挂起直至服务端进程返回数据,所有一个远程方法是很耗时的,那么不能在UI线程中发起此远程请求;由于服务端的Binder运行在Binder的线程池中,所以Binder方法不管是否耗时都采用同步的方式去实现,因为它已经运行在一个线程中了

    Binder工作原理

    Binder死亡代理

    • linkToDeath()
    • unlinkToDeath()

    共享文件

    一个进程序列化对象到sd上的一个文件,另一个进程反序列化sd上的一文件

    Messenger

    底层实现是AIDL,一次处理一个请求,不用考虑线程同步的问题,主要作用是传递消息

    Messenger工作原理

    AIDL

    • 只支持方法,不支持静态常量
    • AIDL的包结构在服务端和客户端要保持一致

    RemoteCallbackList

    • 实现了同步功能
    展开全文
  • 本篇文章主要介绍了 android跨进程通信(IPC):使用AIDL,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 实现简单的Messenger跨进程通信
  • 跨进程通信SendMessage - VB.NET, WM_SETTEXT, WM_GETTEXT
  • 资源为个人收集github上个人觉得最好用的aidl demo,无缝实现android跨进程通信,喜欢的小伙伴可以下载试下,如果觉得不错请点个赞!谢谢
  • Android 跨进程通信 代码~~~~~~~~~~~~~~~~~~~~
  • Android跨进程通信IPC

    千次阅读 2016-10-31 11:13:57
    android跨进程通信(IPC):使用AIDL 使用AIDL实现进程间的通信 Android Service完全解析,关于服务你所需知道的一切(下) Activity与一个远程Service建立关联同样使用AIDL来进行跨进程通信了(IPC)。这是由于...

    参考:

    android中跨进程通讯的4种方式

    android跨进程通IPC):使用AIDL

    使用AIDL实现程间的通信

    Android Service完全解析,关于服务你所需知道的一切()

    Activity与一个远程Service建立关联同样使用AIDL来进行跨进程通信了(IPC这是由于远程的Service是在另一个进程中运行的,因此他们之间的通信是跨进程的通信。

    Android开启多进程模式:

    (1)通过给四大组件指定android:process属性就可以开启多进程模式。若没有为Activity指定process属性,那么它运行在默认进程中,默认进程的进程名为包名。

    进程名以:开头的进程属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中:”的含义是指要在当前的进程名前面附加上当期的包名,这是一种简写的方法。

    而进程名不以:开头的进程属于全局进程,其他应用通过ShareUID方法可以和它跑在同一个进程中。这是一种完整的命名方式,不会附加包名信息。

    android:process=":xyz" //进程名是packageName:xyz
    android:process="aaa.bbb.ccc" //进程名是aaa.bbb.ccc

    (2)Android系统会为每个应用分配一个唯一的UID,具有相同UID的应用才能共享数据。两个应用通过ShareUID跑在同一个进程中是有要求的,需要这两个应用有相同的ShareUID并且签名相同才可以。 在这种情况下,它们可以相互访问对方的私有数据,比如data目录、组件信息等,不管它们是否跑在同一个进程中。如果它们跑在同一个进程中,还可以共享内存数据,它们看起来就像是一个应用的两个部分

    (3)android系统会为每个进程分配一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,所以不同的虚拟机中访问同一个类的对象会产生多个副本。
    (4)使用多进程容易造成以下几个问题:
    1.静态成员和单例模式完全失效;
    2.线程同步机制完全失效:无论锁对象还是锁全局对象都无法保证线程同步;
    3.SharedPreferences的可靠性下降:SharedPreferences不支持并发读写;
    4.Application会多次创建:当一个组件跑在一个新的进程的时候,系统要在创建新的进程的同时分配独立的虚拟机,应用会重新启动一次,也就会创建新的Application运行在同一个进程中的组件是属于同一个虚拟机和同一个Application
    同一个应用的不同组件,如果它们运行在不同进程中,那么和它们分别属于两个应用没有本质区别。

    SerializableParcelable接口可以完成对象的序列化过程,当我们需要通过IntentBinder传递数据时就需要使用Parcelable或者Serializable。还有的时候我们需要把对象持久化到存储设备上或者通过网络传输给其他客户端,这时候也需要使用Serializable来完成对象的持久化。(具体使用见《开发艺术探索》P42

    两者如何选择:SerializableJava中的序列化接口,其使用起来简单但是开销大,序列化和反序列化过程都需要大量I/O操作。ParcelableAndroid中的序列化方式,因此更适合用在Android平台上,它的缺点就是使用起来稍微麻烦些,但是效率很高,这是Android推荐的序列化方式,因此我们要首选ParcelableParcelable主要用在内存序列化上,通过Parcelable将对象序列化到存储设备中或者将对象序列化后通过网络传输也都是可以的,但是这个过程会稍显复杂,因此这两种情况下建议使用的是Serializable

    (1)使用Bundle

    Bundle实现了Parcelable接口,ActivityServiceReceiver都支持在Intent中传递Bundle数据。

    我们可以在Bundle中附加我们需要传输给远程进程的信息并通过Intent发送出去。传输的数据必须可以序列化,比如基本类型、实现了Parcellable接口的对象,实现了Serializable接口的对象以及一些Android支持的特殊对象。

    具体实现参考

    Android中如何使用Bundle传递对象[使用Serializable或者Parcelable]

    Android为什么要设计出Bundle而不是直接使用HashMap来进行数据传递?

    1.Bundle内部是由ArrayMap实现的,ArrayMap的内部实现是两个数组,一个int数组是存储对象数据对应下标,一个对象数组保存key和value,内部使用二分法对key进行排序,所以在添加、删除、查找数据的时候,都会使用二分法查找,只适合于小数据量操作,如果在数据量比较大的情况下,那么它的性能将退化。而HashMap内部则是数组+链表结构,所以在数据量较少的时候,HashMap的Entry Array比ArrayMap占用更多的内存。因为使用Bundle的场景大多数为小数据量,我没见过在两个Activity之间传递10个以上数据的场景,所以相比之下,在这种情况下使用ArrayMap保存数据,在操作速度和内存占用上都具有优势,因此使用Bundle来传递数据,可以保证更快的速度和更少的内存占用。

     

    2.另外一个原因,则是在Android中如果使用Intent来携带数据的话,需要数据是基本类型或者是可序列化类型,HashMap使用Serializable进行序列化,而Bundle则是使用Parcelable进行序列化。而在Android平台中,更推荐使用Parcelable实现序列化,虽然写法复杂,但是开销更小,所以为了更加快速的进行数据的序列化和反序列化,系统封装了Bundle类,方便我们进行数据的传输。

    (2)使用文件共享

    种方式简单,适合在对数据同步要求不高的进程之间进行通信,并且要妥善处理并发读写的问题。通过文件共享这种方式来共享数据度文件格式是没有具体要求的,比如可以是文本文件,也可以是XML文件,只要读/写双发约定数据格式即可。

    SharedPreferences是一个特例,虽然它也是文件的一种,但是由于系统对它的读写有一定的缓存策略,即在内存中会有一份SharedPreferences文件的缓存,因此在多进程模式下系统对它的读写就变得不可靠,当面对高并发读写访问的时候,有很大几率会丢失数据,因此,不建议在进程间通信中使用SharedPreferences

    (3)使用Messenger

    Messenger是一种轻量级的IPC方案,它的底层实现就是AIDLMessenger是以串行的方式处理请求的,即服务端只能一个个处理,不存在并发执行的情形,详细的示例见原书。

    Messenger中进行数据传递必须将数据放入Message中,而MessengerMessage都实现了Parcelable接口,因此都可以跨进程传输。简单来说,Message中所支持的数据类型就是所支持的传输类型。实际上,通过Messenger来传递MessageMessage中能使用的载体只有what,arg1,arg2,Bundle以及replyto.Message中的另一个字段Object在同进程中是很实用的,但是在进程间通信的时候,在Android2.2之前object字段不支持跨进程传输,即便在2.2以后,也仅仅是系统提供的实现了Parcelable接口的对象才能通过它来传输。这就意味着我们自定义的Parcelable对象是无法通过object字段来传输的。

    (4)使用AIDL

    Messenger是以串行的方式处理客户端发来的信息,如果大量的信息同时发送到服务端时,服务端仍然只能一个个处理,如果有大量的并发请求,那么Messenger就不太合适了。同时Messenger的作用主要是为了传递信息,很多时候我们可能需要跨进程调用服务端的方法,这种情形Messenger就无法做到了,但是我们可以利用AIDL来实现跨进程的方法调用。

    AIDLAndroid Interface Definition Language)是Android接口定义语言的意思,它可以用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。

    大致流程分为服务端和客户端两个方面(具体示例见《开发艺术探索》p71):

    服务端:服务端首先要创建一个Service来监听客户端的连接请求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,然后在Service中实现这个AIDL即可。

    客户端:客户端所要做的事情就稍微简单一些,首先需要绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了。

    1.AIDL支持的数据类型:基本数据类型、StringCharSequenceArrayListHashMapParcelable以及AIDL
    2.以上6种类型的数据就是AIDL所支持的所有类型,其中自定义的Parcelable对象和AIDL对象必须要显式的import进来,不管它们是否和当前的AIDL文件位于同一个包内。如果AIDL文件中用到了自定义的Parcelable对象那么必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型。(具体实例代码见《开发艺术探索》P73
    3.AIDL中除了基本数据类,其他类型的参数都要标上方向:inout或者inout
    4.AIDL接口中支持方法,不支持声明静态变量;
    5.为了方便AIDL的开发,建议把所有和AIDL相关的类和文件全部放入同一个包中,这样做的好处是,当客户端是另一个应用的时候,可以直接把整个包复制到客户端工程中。
    6.RemoteCallbackList是系统专门提供的用于删除跨进程Listener的接口。RemoteCallbackList是一个泛型,支持管理任意的AIDL接口,因为所有的AIDL接口都继承自IInterface接口。

    AIDL:Android Interface Definition Language,即Android接口定义语言。

    Android 使用AIDL提供公开服务接口,使得不同进程间可以相互通信。

    aidl对应的接口名称必须与aidl文件名相同不然无法自动编译

    aidl对应的接口的方法不能加访问权限修饰符(记一下)

    建立AIDL服务要比建立普通的服务复杂一些,具体步骤如下:

    1)在Eclipse Android工程的Java包目录中建立一个扩展名为aidl的文件。该文件的语法类似于Java代码,但会稍有不同。

    2)如果aidl文件的内容是正确的,ADT会自动生成一个Java接口文件(*.java)。

    3)建立一个服务类(Service的子类)。

    4)实现由aidl文件生成的Java接口。

    5)在AndroidManifest.xml文件中配置AIDL服务,尤其要注意的是,<action>标签中android:name的属性值就是客户端要引用该服务的ID,也就是Intent类的参数值。

    (5)使用ContentProvider

    专门用于不同应用间进行数据共享的方式,底层实现为Binder
    1.ContentProvider主要以表格的形式来组织数据,并且可以包含多个表;
    2.ContentProvider还支持文件数据,比如图片、视频等,系统提供的MediaStore就是文件类型的ContentProvider
    3.ContentProvider对底层的数据存储方式没有任何要求,可以是SQLite、文件,甚至是内存中的一个对象都行;
    4.要观察ContentProvider中的数据变化情况,可以通过ContentResolverregisterContentObserver方法来注册观察者;

    (6)使用Socket

    Socket是网络通信中“套接字”的概念,分为流式套接字和用户数据包套接字两种,分别对应网络的传输控制层的TCP和UDP协议。

    Binder连接池

    (1)当项目规模很大的时候,创建很多个Service是不对的做法,因为service是系统资源,太多的service会使得应用看起来很重,所以最好是将所有的AIDL放在同一个Service中去管理。

    整个工作机制是:每个业务模块创建自己的AIDL接口并实现此接口,这个时候不同业务模块之间是不能有耦合的,所有实现细节我们要单独开来,然后向服务端提供自己的唯一标识和其对应的Binder对象;对于服务端来说,只需要一个Service,服务端提供一个queryBinder接口,这个接口能够根据业务模块的特征来返回相应的Binder对象给它们,不同的业务模块拿到所需的Binder对象后就可以进行远程方法调用了。

    Binder连接池的主要作用就是将每个业务模块的Binder请求统一转发到远程Service去执行,从而避免了重复创建Service的过程。
    (2)作者实现的Binder连接池BinderPool的实现源码,建议在AIDL开发工作中引入BinderPool机制。



    展开全文
  • Android跨进程通信Binder、Messenger、AIDL汇总,代码包含服务端和客户端。一站式解决。
  • Android IPC 之 AIDL 跨进程通信

    千次阅读 2020-04-09 14:15:37
    文章目录IPC 简介IPC 基础,主要包含三个方面:1,Serializable 接口2,Parcelable 接口3,Binder使用 AIDL...IPC 的含义为进程间通信或者跨进程通信,是指两个进程之间数据交换的过程 多进程的情况分为两种: 1,一个...
  • android开发使用aidl进行跨进程通信demo
  • 使用AIDL实现两个APP之间跨进程通信

    千次阅读 2019-07-11 15:34:47
    昨天我们主管说准备把项目拆分一下,因为现在项目依赖了好几个负责串口通讯...然后让我写一个跨进程通信的Demo,然后测试。 一、同一个APP内跨进程通信 跨进程通信的方式有好几种,我这里用的是AIDL的方式。 ...
  • 安卓跨进程通信之AIDL使用入门,对应讲解博客:http://blog.csdn.net/smarticeberg/article/details/50972211
  • 本文为大家分享了Android AIDL实现两个APP间的跨进程通信实例,供大家参考,具体内容如下 1 Service端创建 首先需要创建一个Android工程然后创建AIDL文件,创建AIDL文件主要为了生成继承了Binder的Stub类,以便应用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 109,768
精华内容 43,907
关键字:

跨进程通信