谈谈Android Binder机制及AIDL使用

Binder原理

一、概述

Android系统中,涉及到多进程间的通讯底层都是依赖于Binder IPC机制。例如当进 程A中的Activity要向进程B中的Service通讯,这便须要依赖于Binder IPC。不只于 此,整个Android系统架构中,大量采用了Binder机制做为IPC(进程间通讯, Interprocess Communication)方案。 android

固然也存在部分其余的IPC方式,如管道、SystemV、Socket等。那么Android为什 么不使用这些原有的技术,而是要使开发一种新的叫Binder的进程间通讯机制呢?git

顺手留下GitHub连接,须要获取相关面试等内容的能够本身去找
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)github

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

安全方面
传统的进程通讯方式对于通讯双方的身份并无作出严格的验证,好比Socket通讯 的IP地址是客户端手动填入,很容易进行伪造。然而,Binder机制从协议自己就支 持对通讯双方作身份校检,从而大大提高了安全性。安全

二、 Binder

IPC原理
从进程角度来看IPC(Interprocess Communication)机制
谈谈Android Binder机制及AIDL使用
每一个Android的进程,只能运行在本身进程所拥有的虚拟地址空间。例如,对应一 个4GB的虚拟地址空间,其中3GB是用户空间,1GB是内核空间。固然内核空间的 大小是能够经过参数配置调整的。对于用户空间,不一样进程之间是不能共享的,而 内核空间倒是可共享的。Client进程向Server进程通讯,偏偏是利用进程间可共享 的内核内存空间来完成底层通讯工做的。Client端与Server端进程每每采用ioctl等方 法与内核空间的驱动进行交互。架构

Binder原理
Binder通讯采用C/S架构,从组件视角来讲,包含Client、Server、ServiceManager 以及Binder驱动,其中ServiceManager用于管理系统中的各类服务。架构图以下所 示:
谈谈Android Binder机制及AIDL使用
Binder通讯的四个角色 ide

Client进程: 使用服务的进程。
Server进程: 提供服务的进程。
ServiceManager进程: ServiceManager的做用是将字符形式的Binder名字转化成 Client中对该Binder的引用,使得Client可以经过Binder名字得到对Server中Binder 实体的引用。
Binder驱动: 驱动负责进程之间Binder通讯的创建,Binder在进程之间的传递, Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。函数

Binder运行机制
图中Client/Server/ServiceManage之间的相互通讯都是基于Binder机制。既然基于 Binder机制通讯,那么一样也是C/S架构,则图中的3大步骤都有相应的Client端与 Server端。布局

注册服务(addService): Server进程要先注册Service到ServiceManager。该过 程:Server是客户端,ServiceManager是服务端。
获取服务(getService): Client进程使用某个Service前,须先向ServiceManager中 获取相应的Service。该过程:Client是客户端,ServiceManager是服务端。
使用服务: Client根据获得的Service信息创建与Service所在的Server进程通讯的通 路,而后就能够直接与Service交互。该过程:Client是客户端,Server是服务端。性能

图中的ClientServerService Manager之间交互都是虚线表示,是因为它们彼此 之间不是直接交互的,而是都经过与Binder驱动进行交互的,从而实现IPC通讯 (Interprocess Communication)方式。其中Binder驱动位于内核空间,ClientServerService Manager位于用户空间。Binder驱动和Service Manager能够看作 是Android平台的基础架构,而Client和Server是Android的应用层,开发人员只需自 定义实现Client、Server端,借助Android的基本平台架构即可以直接进行IPC通 信。

Binder运行的实例解释
首先咱们看看咱们的程序跨进程调用系统服务的简单示例,实现浮动窗口部分代 码:

//获取WindowManager服务引用 
  WindowManager wm = (WindowManager) getSystemService(getApplicati on().WINDOW_SERVICE); 
  //布局参数layoutParams相关设置略... 
  View view = LayoutInflater.from(getApplication()).inflate(R.layo ut.float_layout, null); 
  //添加view 
  wm.addView(view, layoutParams);

注册服务(addService): 在Android开机启动过程当中,Android会初始化系统的各类 Service,并将这些Service向ServiceManager注册(即让ServiceManager管理)。 这一步是系统自动完成的。
获取服务(getService): 客户端想要获得具体的Service直接向ServiceManager要 便可。客户端首先向ServiceManager查询获得具体的Service引用,一般是Service 引用的代理对象,对数据进行一些处理操做。即第2行代码中,获得的wm是 WindowManager对象的引用。
使用服务: 经过这个引用向具体的服务端发送请求,服务端执行完成后就返回。即 第6行调用WindowManageraddView函数,将触发远程调用,调用的是运行在 systemServer进程中的WindowManageraddView函数。
使用服务的具体执行过程
谈谈Android Binder机制及AIDL使用

  1. Client经过得到一个Server的代理接口,对Server进行调用。
  2. 代理接口中定义的方法与Server中定义的方法是一一对应的。
  3. Client调用某个代理接口中的方法时,代理接口的方法会将Client传递的参数打 包成Parcel对象。
  4. 代理接口将Parcel发送给内核中的Binder Driver。
  5. Server会读取Binder Driver中的请求数据,若是是发送给本身的,解包Parcel 对象,处理并将结果返回。
  6. 整个的调用过程是一个同步过程,在Server处理的时候,Client会Block住。因 此Client调用过程不该在主线程。

AIDL的使用

1.AIDL的简介

AIDL (Android Interface Definition Language) 是一种接口定义语言,用于生成能够 在Android设备上两个进程之间进行进程间通讯(Interprocess Communication, IPC) 的代码。若是在一个进程中(例如Activity)要调用另外一个进程中(例如Service) 对象的操做,就可使用AIDL生成可序列化的参数,来完成进程间通讯。

简言之,AIDL可以实现进程间通讯,其内部是经过Binder机制来实现的,后面会 具体介绍,如今先介绍AIDL的使用。

2.AIDL的具体使用

AIDL的实现一共分为三部分,一部分是客户端,调用远程服务。一部分是服务端, 提供服务。最后一部分,也是最关键的是AIDL接口,用来传递的参数,提供进程间 通讯。
先在服务端建立AIDL部分代码。
AIDL文件 经过以下方式新建一个AIDL文件
谈谈Android Binder机制及AIDL使用
默认生成格式

interface IBookManager { 
      /**
       * Demonstrates some basic types that you can use as paramet ers 
       * and return values in AIDL. 
       */ 
     void basicTypes(int anInt, long aLong, boolean aBoolean, flo at aFloat, double aDouble, String aString);
   }

默认以下格式,因为本例要操做Book类,实现两个方法,添加书本和返回书本列 表。
定义一个Book类,实现Parcelable接口。

public class Book implements Parcelable { 
     public int bookId; 
     public String bookName; 

     public Book() { 
     }

     public Book(int bookId, String bookName) { 
        this.bookId = bookId; 
        this.bookName = bookName; 
     }

     public int getBookId() { 
        return bookId; 
     }

     public void setBookId(int bookId) { 
        this.bookId = bookId; 
     }
     public String getBookName() { 
        return bookName; 
     }
     public void setBookName(String bookName) { 
        this.bookName = bookName; 
     }

     @Override 
     public int describeContents() { 
        return 0; 
     }

     @Override 
     public void writeToParcel(Parcel dest, int flags) { 
        dest.writeInt(this.bookId); 
        dest.writeString(this.bookName); 
     }

     protected Book(Parcel in) { 
        this.bookId = in.readInt(); 
        this.bookName = in.readString(); 
     }
     public static final Parcelable.Creator<Book> CREATOR = new P arcelable.Creator<Book>() {
        @Override 
        public Book createFromParcel(Parcel source) { 
           return new Book(source); 
        }

        @Override 
        public Book[] newArray(int size) { 
           return new Book[size]; 
        } 
    }; 
  }

因为AIDL只支持数据类型:基本类型(int,long,char,boolean等),String, CharSequence,List,Map,其余类型必须使用import导入,即便它们可能在同一 个包里,好比上面的Book。 最终IBookManager.aidl 的实现

// Declare any non-default types here with import statements import com.lvr.aidldemo.Book; 
  interface IBookManager { 
     /**
       * Demonstrates some basic types that you can use as paramet ers 
       * and return values in AIDL. 
       */ 
    void basicTypes(int anInt, long aLong, boolean aBoolean, flo at aFloat, double aDouble, String aString); 
    void addBook(in Book book); List<Book> getBookList(); 
  }

注意: 若是自定义的Parcelable对象,必须建立一个和它同名的AIDL文件,并在其 中声明它为parcelable类型。

Book.aidl

// Book.aidl 
  package com.lvr.aidldemo; 

  parcelable Book;

以上就是AIDL部分的实现,一共三个文件。 而后Make Project ,SDK为自动为咱们生成对应的Binder类。 在以下路径下:
谈谈Android Binder机制及AIDL使用
其中该接口中有个重要的内部类Stub ,继承了Binder 类,同时实现了 IBookManager接口。 这个内部类是接下来的关键内容。

public static abstract class Stub extends android.os.Binder impl ements com.lvr.aidldemo.IBookManager{}

服务端 服务端首先要建立一个Service用来监听客户端的链接请求。而后在Service 中实现Stub 类,并定义接口中方法的具体实现。

//实现了AIDL的抽象函数 
  private IBookManager.Stub mbinder = new IBookManager.Stub() { 
     @Override 
     public void basicTypes(int anInt, long aLong, boolean aBoole an, float aFloat, double aDouble, String aString) throws RemoteE xception {
        //什么也不作 
     }

     @Override 
     public void addBook(Book book) throws RemoteException { 
        //添加书本 
        if (!mBookList.contains(book)) { 
             mBookList.add(book); } 
     }

     @Override 
     public List<Book> getBookList() throws RemoteException { 
       return mBookList; 
    } 
  };

当客户端链接服务端,服务端就会调用以下方法:

public IBinder onBind(Intent intent) { 
     return mbinder; 
  }

就会把Stub实现对象返回给客户端,该对象是个Binder对象,能够实现进程间通 信。 本例就不真实模拟两个应用之间的通讯,而是让Service另外开启一个进程来 模拟进程间通讯。

<service 
      android:name=".MyService" 
      android:process=":remote"> 
      <intent-filter> 
          <category android:name="android.intent.category.DEFAULT" /> 
          <action android:name="com.lvr.aidldemo.MyService" /> 
      </intent-filter> 
  </service>

android:process=":remote"设置为另外一个进程。 &lt;action android:name="com.lvr.aidldemo.MyService"/&gt; 是为了能让其余apk隐式 bindService。经过隐式调用的方式来链接service,须要把category设为default, 这是由于,隐式调用的时候,intent中的category默认会被设置为default。

客户端
首先将服务端工程中的aidl文件夹下的内容整个拷贝到客户端工程的对应位置下, 因为本例的使用在一个应用中,就不须要拷贝了,其余状况必定不要忘记这一步。
客户端须要作的事情比较简单,首先须要绑定服务端的Service。

Intent intentService = new Intent(); 
  intentService.setAction("com.lvr.aidldemo.MyService"); 
  intentService.setPackage(getPackageName()); 
  intentService.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
  MyClient.this.bindService(intentService, mServiceConnection, BIN 
  D_AUTO_CREATE); 
  Toast.makeText(getApplicationContext(), "绑定了服务", Toast.LENGTH _SHORT).show();

将服务端返回的Binder对象转换成AIDL接口所属的类型,接着就能够调用AIDL中的 方法了。

if (mIBookManager != null) { 
     try {
          mIBookManager.addBook(new Book(18, "新添加的书")); 
          Toast.makeText(getApplicationContext(), mIBookManager.ge 
  tBookList().size() + "", Toast.LENGTH_SHORT).show(); 
     } catch (RemoteException e) {
     e.printStackTrace(); 
     } 
  }
3.AIDL的工做原理

Binder机制的运行主要包括三个部分:注册服务、获取服务和使用服务。 其中注册 服务和获取服务的流程涉及C的内容,因为我的能力有限,就不予介绍了。
本篇文章主要介绍使用服务时,AIDL的工做原理。

①.Binder对象的获取
Binder是实现跨进程通讯的基础,那么Binder对象在服务端和客户端是共享的,是 同一个Binder对象。在客户端经过Binder对象获取实现了IInterface接口的对象来调 用远程服务,而后经过Binder来实现参数传递。

那么如何维护实现了IInterface接口的对象和获取Binder对象呢?
服务端获取Binder对象并保存IInterface接口对象 Binder中两个关键方法:

public class Binder implement IBinder { 
     void attachInterface(IInterface plus, String descriptor) 
     IInterface queryLocalInterface(Stringdescriptor) //从IBinder 中继承而来 
    ......
   }

Binder具备被跨进程传输的能力是由于它实现了IBinder接口。系统会为每一个实现了 该接口的对象提供跨进程传输,这是系统给咱们的一个很大的福利。

Binder具备的完成特定任务的能力是经过它的IInterface的对象得到的,咱们能够 简单理解attachInterface方法会将(descriptor,plus)做为(key,value)对存入 Binder对象中的一个Map对象中,Binder对象可经过attachInterface方法持有一个 IInterface对象(即plus)的引用,并依靠它得到完成特定任务的能力。 queryLocalInterface方法能够认为是根据key值(即参数 descriptor)查找相应的 IInterface对象。 在服务端进程,经过实现 private IBookManager.Stub mbinder = new IBookManager.Stub() {}抽象类,得到Binder对象。 并保存了IInterface对象。

public Stub() { 
     this.attachInterface(this, DESCRIPTOR); 
  }

客户端获取Binder对象并获取IInterface接口对象 经过bindService得到Binder对象

MyClient.this.bindService(intentService, mServiceConnection, BIN D_AUTO_CREATE);

而后经过Binder对象得到IInterface对象。

private ServiceConnection mServiceConnection = new ServiceConnec tion() { 
     @Override 
     public void onServiceConnected(ComponentName name, IBinder b inder) {
       //经过服务端onBind方法返回的binder对象获得IBookManager的实例, 获得实例就能够调用它的方法了 
       mIBookManager = IBookManager.Stub.asInterface(binder); 
     }
     @Override 
     public void onServiceDisconnected(ComponentName name) { 
     mIBookManager = null; 
     } 
  };

其中 asInterface(binder)方法以下:

public static com.lvr.aidldemo.IBookManager asInterface(android. os.IBinder obj) { 
     if ((obj == null)) { 
         return null; 
     }
     android.os.IInterface iin = obj.queryLocalInterface(DESCRIPT OR);
     if (((iin != null) && (iin instanceof com.lvr.aidldemo.IBook Manager))) { 
         return ((com.lvr.aidldemo.IBookManager) iin); 
     }
     return new com.lvr.aidldemo.IBookManager.Stub.Proxy(obj); 
   }

先经过 queryLocalInterface(DESCRIPTOR);查找到对应的IInterface对象,而后 判断对象的类型,若是是同一个进程调用则返回IBookManager对象,因为是跨进 程调用则返回Proxy对象,即Binder类的代理对象。

②.调用服务端方法
得到了Binder类的代理对象,而且经过代理对象得到了IInterface对象,那么就能够 调用接口的具体实现方法了,来实现调用服务端方法的目的。 以addBook方法为例,调用该方法后,客户端线程挂起,等待唤醒:

@Override public void addBook(com.lvr.aidldemo.Book book) th rows android.os.RemoteException 
  { 
      .......... 
      //第一个参数:识别调用哪个方法的ID 
     //第二个参数:Book的序列化传入数据 
     //第三个参数:调用方法后返回的数据 //最后一个不用管 
     mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply , 0); 
     _reply.readException(); 
     }
     .......... 
  }

省略部分主要完成对添加的Book对象进行序列化工做,而后调用 transact 方 法。

Proxy对象中的transact调用发生后,会引发系统的注意,系统意识到Proxy对象想 找它的真身Binder对象(系统其实一直存着Binder和Proxy的对应关系)。因而系统 将这个请求中的数据转发给Binder对象,Binder对象将会在onTransact中收到Proxy 对象传来的数据,因而它从data中取出客户端进程传来的数据,又根据第一个参数 肯定想让它执行添加书本操做,因而它就执行了响应操做,并把结果写回reply。代 码概略以下:

case TRANSACTION_addBook: { 
      data.enforceInterface(DESCRIPTOR); 
      com.lvr.aidldemo.Book _arg0; 
      if ((0 != data.readInt())) { 
          _arg0 = com.lvr.aidldemo.Book.CREATOR.createFromParcel(d ata);
      } else { 
          _arg0 = null; 
      }
     //这里调用服务端实现的addBook方法 
     this.addBook(_arg0); 
     reply.writeNoException(); 
     return true; 
  }

而后在 transact 方法得到 _reply 并返回结果,本例中的addList方法没有返回 值。

客户端线程被唤醒。所以调用服务端方法时,应开启子线程,防止UI线程堵塞,导 致ANR。
顺手留下GitHub连接,须要获取相关面试等内容的能够本身去找
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)

PDF和源码获取

谈谈Android Binder机制及AIDL使用

相关文章
相关标签/搜索