安卓IPC机制之Binder详解

IPC(Inter-Process Communication,跨进程通信)是指两个进程之间数据交换的过程,因此我们首先必须了解什么是进程,什么是线程。

进程:进程是正在运行的程序的实例,与程序相比,它更强调动态的概念,与线程相比,进程是线程的容器,一个进程可以包含多个线程但至少包含一个线程。进程是任务调度的基本单位,是系统资源的分配单位。

线程:线程是进程中的一条执行路径,它只能隶属于某一个进程,在支持多线程的操作系统或程序设计语言中(如java),线程是任务调度的单位,但它不是系统资源分配的单位,线程完全继承父进程占有的资源,但它执行时,也有属于自己的运行现场,如PC寄存器的值,栈空间等。

关于进程与线程内存资源方面的知识请参看我的博文:http://blog.csdn.net/htq__/article/details/50822582

IPC机制不是安卓独有的,任何一个操作系统都需要IPC机制,如linux中可以通过命名管道,共享主存,信号量,Socket,消息队列等方式来呢实现IPC机制,安卓虽然是基于linux内核的,但是安卓中的IPC机制与linux不完全相同,如Binder机制就是安卓的IPC中的一大特色,这种IPC机制在linux中是不存在的。本博客将详细讲解安卓中的Binder机制。

在安卓开发中Binder主要用在Service中,包括AIDL与Messenger,而Messenger的底层仍然是AIDL,关于这些内容,读者可以参看我的博客:点击打开链接。所以本博客将以AIDL为例详细讲解Binder的IPC原理。

首先创建一个AIDL的java包,在该包中创建三个文件Book.java,Book.aidl和IBookManager.aidl代码如下:

public class Book implements Parcelable {  //Book.java

    public int bookId;
    public String bookName;

    public Book() {

    }

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

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(bookId);
        out.writeString(bookName);
    }

    public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

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

    private Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    @Override
    public String toString() {
        return String.format("[bookId:%s, bookName:%s]", bookId, bookName);
    }

}

parcelable Book;//Book.aidl

interface IBookManager {  //IBookManager.aidl
     List<Book> getBookList();
     void addBook(in Book book);
}

其中Book.java是一个表示图书信息的java类,它实现了Parcelable接口,Book.aidl是Book类在AIDL中的声明,IBookManager.aidl是我们定义的一个接口,注意尽管Book类与IBookManager类位于同一个包中,但是在IBookManager.aidl文件中仍需要导入Book类,这是aidl文件与普通java的class文件的不同之处。

当我们定义好上述文件后Eclipse会自动为我们在工程的gen目录下生成一个与IBookManager.aidl文件相对应的java类IBookManager.java,我们来看一下其源码:

public interface IBookManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.test.aidl.IBookManager
{
private static final java.lang.String DESCRIPTOR = "com.test.aidl.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.test.aidl.IBookManager interface,
 * generating a proxy if needed.
 */
public static com.test.aidl.IBookManager asInterface(android.os.IBinder obj)
{
   if ((obj==null)) {
    return null;
    }
	android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
	  if (((iin!=null)&&(iin instanceof com.test.aidl.IBookManager))) {
	  return ((com.test.aidl.IBookManager)iin);
	}
        return new com.test.aidl.IBookManager.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
{
	switch (code)
    {
	case INTERFACE_TRANSACTION:
      {
	   reply.writeString(DESCRIPTOR);
          return true;
      }
       case TRANSACTION_getBookList:
     {
 	data.enforceInterface(DESCRIPTOR);
	java.util.List<com.test.aidl.Book> _result = this.getBookList();
	reply.writeNoException();
	reply.writeTypedList(_result);
	return true;
      }
      case TRANSACTION_addBook:
     {
	data.enforceInterface(DESCRIPTOR);
	com.test.aidl.Book _arg0;
	if ((0!=data.readInt())) {
		_arg0 = com.test.aidl.Book.CREATOR.createFromParcel(data);
	}
	else {
	_arg0 = null;
	}
	this.addBook(_arg0);
	reply.writeNoException();
	return true;
	}
      }
	return super.onTransact(code, data, reply, flags);
}

private static class Proxy implements com.test.aidl.IBookManager
{
      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 java.util.List<com.test.aidl.Book> getBookList() throws android.os.RemoteException
     {
	android.os.Parcel _data = android.os.Parcel.obtain();
	android.os.Parcel _reply = android.os.Parcel.obtain();
	java.util.List<com.test.aidl.Book> _result;
	try {
		_data.writeInterfaceToken(DESCRIPTOR);
		mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
		_reply.readException();
		_result = _reply.createTypedArrayList(com.test.aidl.Book.CREATOR);
	}
	finally {
	_reply.recycle();
	_data.recycle();
	}
	return _result;
     }

@Override
public void addBook(com.test.aidl.Book book) throws android.os.RemoteException
{
   android.os.Parcel _data = android.os.Parcel.obtain();
   android.os.Parcel _reply = android.os.Parcel.obtain();
  try {
   _data.writeInterfaceToken(DESCRIPTOR);
   if ((book!=null)) {
     _data.writeInt(1);
      book.writeToParcel(_data, 0);
      }
   else {
     _data.writeInt(0);
   }
     mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
     _reply.readException();
  }
     finally {
    _reply.recycle();
     _data.recycle();
     }
   }
}
	static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
	static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

	public java.util.List<com.test.aidl.Book> getBookList() throws android.os.RemoteException;
	public void addBook(com.test.aidl.Book book) throws android.os.RemoteException;

}

可以看到系统为我们自动创建的IBookManager文件仍然是一个接口,它继承自android.os.IInterface。在其内部创建了一个抽象的静态类Stub,它继承自Binder类同时实现了IBookManager接口(但未实现该接口中的方法,因为该接口中的方法是我们在创建一个Service时在其内部创建Binder类时实现的,因此该Stub仍然属于一个抽象类)。

我们首先来梳理一下IBookManager接口的内容,可以看到,首先包含两个方法的声明getBookList和addBook,显然这两个方法是我们在IBookManager.aidl文件中声明的两个方法,另外它还定义了两个整型的id用于标识这两个方法,这两个id在其内部代理类Proxy的方法的transact过程中用于判断客户端请求的是哪一个方法。另外还包含一个内部类Stub,该Stub是一个Binder类,在这个Stub类中定义了一个内部类Proxy。

下面介绍一下上述代码中的一些重要方法:

public static com.test.aidl.IBookManager asInterface(android.os.IBinder obj)
{
   if ((obj==null)) {
    return null;
    }
	android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
	  if (((iin!=null)&&(iin instanceof com.test.aidl.IBookManager))) {//此时表示客户端与服务端位于同一个进程中,直接返回IInterface对象
	  return ((com.test.aidl.IBookManager)iin);
	}
        return new com.test.aidl.IBookManager.Stub.Proxy(obj);<span style="font-family: Arial, Helvetica, sans-serif;">//此时表示客户端与服务端不在同一个进程中,此时返回代理类Proxy对象</span>

 }

asInterface正是我们在bindServide的组件中使用ServiceConnection时首先要使用的一个方法,可以看到在方法中首先通过obj.queryLocalInterface(DESCRIPTOR)根据传入的参数获取IInterface对象,然后判断该对象是否为IBookManager类,若是则直接返回该对象,此时表示客户端与服务端位于同一个进程中,即绑定的是本地服务,即在ServiceConnection的onServiceConnected中我们是这么调用的Binder对象

public void onServiceConnected(ComponentName name, IBinder service) {
            myBinder = (MyService.MyBinder) service;   //绑定的是本地服务
            myBinder.doSomething();
        }  

否则返回IBookManager类的内部代理对象IBookManager.Stub.Proxy(obj),也就意味着客户端与服务端位于不同的进程中,此时客户端调用的方法将会是Proxy代理类中定义的方法,而不是直接调用的在Service中实现的IBookManager.aidl文件中的方法。即绑定的是远程服务,即在ServiceConnection的onServiceConnected中我们是这么调用的Binder对象

public void onServiceConnected(ComponentName name, IBinder service) {
            myBinder = MyAIDL.Stub.asInterface(service)  //绑定的是远程服务
            myBinder.doSomething();
        }   

通过上面的分析可知,当绑定的是远程服务时,当在客户端调用addBook将会调用代理类中的 addBook方法,因此我们接下来看一下Proxy#addBook

@Override
public void addBook(com.test.aidl.Book book) throws android.os.RemoteException
{
   android.os.Parcel _data = android.os.Parcel.obtain();
   android.os.Parcel _reply = android.os.Parcel.obtain();
  try {
   _data.writeInterfaceToken(DESCRIPTOR);
   if ((book!=null)) {
     _data.writeInt(1);
      book.writeToParcel(_data, 0);
      }
   else {
     _data.writeInt(0);
   }
     mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
     _reply.readException();
  }
     finally {
    _reply.recycle();
     _data.recycle();
     }
 }

可以看到在该方法中首先创建了一个输入型参数Parce对象_data与输出型Parcel对象_reply,然后向该输入型参数Parce对象_data中写入一些数据,然后调用了mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);语句,当执行该语句时,即表示客户端发起RPC(远程过程调用)请求,此时当前线程会被挂起,直至RPC过程返回后,当前线程继续执行。注意该mRemote对象即为在创建该Proxy对象时传入的Binder对象,即mRemote是一个Binder对象,我们看一下Binder中的transact函数,代码如下:

 /**
     * Default implementation rewinds the parcels and calls onTransact.  On
     * the remote side, transact calls into the binder to do the IPC.
     */
    public final boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);
        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }

可以看到transact函数被final修饰,即该函数是不允许被继承重写的,在该函数中最重要的一个语句: boolean r = onTransact(code, data, reply, flags);即在transact函数中调用了onTransact函数,而onTransact函数一般是要求被重写的,而重写的过程是根据我们自己定义的aidl文件IDE自动帮我们完成的,如上面所示的根据IBookManager.aidl文件自动生成IBookManager.java文件,在这个IBookManager.java文件中onTransact函数被重写。

这个onTransact函数是Binder机制运行的关键,该函数运行在服务端的Binder线程池中,当客户端发起跨进程请求时,即调用transact函数时,远程请求的数据会通过系统底层交给该函数来执行,该函数的完整签名为: public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags),服务端通过code可以确定客户端调用的是那个方法,然后从data中取出目标方法所带的参数(如果目标方法带参数的话),然后执行目标方法,当目标方法执行完毕后,就向reply中写入返回值(如果目标方法存在返回值的话),需要注意的是如果该函数返回false,则客户端的请求会失败,利用该特性可以做权限验证,因为我们也不希望任意一个进程都可以随意访问我们的服务。

通过上述的分析相信读者对Binder的运行机制的整个过程已经非常熟悉了,至于系统底层的Binder是如何传输数据的这个不重要,也不需要我们去理解,我们只需要知道Binder的运行机制的整个过程,下面用图示的方式来讲上述过程表示出来。注意下面图示仅仅表示绑定远程服务的过程,因为绑定本地服务不涉及IPC机制。如下:

对上述图示还是稍作解释,首先在客户端Client的ServiceConnection的onServiceConnected(ComponentName name, IBinder service) 方法中我们是通过   myBinder = MyAIDL.Stub.asInterface(service)语句将service转换为Binder对象,即调用的是asInterface方法而不是使用(MyService.MyBinder) service的类型转换的方式,则表示我们要获取的是远程服务而不是本地服务,那么接下来 myBinder调用的addBook(book);这个方法将会在其内部调用Binder的transact方法用来发起RPC请求,此时客户端Client被挂起,Binder的transact方法是一个final方法,不允许被修改,在该方法中调用了onTransact方法,该方法运行在服务端的Binder线程池中,该方法是整个Binder机制运行的关键,在该方法中完成IPC数据的读取与写入,该方法返回值将写入_reply参数中,然后RPC过程结束,返回数据给客户端,客户端Client被唤醒。

另外因为服务端的Binder方法是运行在Binder线程池中的,所以Binder中的方法不管是否耗时都应该采用同步的方式去实现,因为它已经运行在一个线程中。

时间: 2024-10-10 04:38:51

安卓IPC机制之Binder详解的相关文章

[gitbook] Android框架分析系列之Android Binder详解

请支持作者原创: https://mr-cao.gitbooks.io/android/content/android-binder.html Android Binder详解 Table of Contents 1. binder简介 2. binder的实现 2.1. IBinder类简介 2.2. IInterface类简介 2.3. BpBinder和BBinder简介 2.4. ProcessState和IPCThreadState简介 2.5. ServiceManager简介 2.

[Android 新特性] 安卓4.4新特性详解

在本月初,谷歌的5太子google  nexus5正式发布了,还给大家带来了全新的安卓android4.4的操作系统,并且官网给大家带来了新的说法,就是安卓4.4会比之前的系统更加省电,这个到底是为什么呢?网侠小编就来说说安卓4.4的为何省电的工作机制. 首先,我们要搞清楚一个问题,那就是什么是Dalvik? 在整个Android 4.4的更新特性中,最引人瞩目的应该就是虚拟机由Dalvik转成ART,也就是“中间件”的变更(Midware).先来看看什么是Dalvik. 如图,Dalvik虚拟

Android View 事件分发机制源码详解(View篇)

前言 在Android View 事件分发机制源码详解(ViewGroup篇)一文中,主要对ViewGroup#dispatchTouchEvent的源码做了相应的解析,其中说到在ViewGroup把事件传递给子View的时候,会调用子View的dispatchTouchEvent,这时分两种情况,如果子View也是一个ViewGroup那么再执行同样的流程继续把事件分发下去,即调用ViewGroup#dispatchTouchEvent:如果子View只是单纯的一个View,那么调用的是Vie

ARC机制之__strong详解

ARC机制之__strong详解 __strong  解析: 默认情况下,一个指针都会使用 __strong 属性,表明这是一个强引用.这意味着,只要引用存在,对象就不能被销毁.这是一种所期望的行为:当所有(强)引用都去除时,对象才能被收集和释放. 不过, 有时我们却希望禁用这种行为:一些集合类不应该增加其元素的引用,因为这会引起对象无法释放.在这种情况下,我们需要使用弱引用(不用担心,内置的集合类 就是这么干的),使用 __weak 关键字.NSHashTable 就是一个例子.当被引用的对象

[安卓基础] 009.组件Activity详解

*:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } a { color: #4183C4; text-decoration: none; } a.absent { color: #cc0000; } a.anchor { display: block; padding-left: 30px; margin-left: -30px; cursor: poin

深入理解Android IPC机制之Binder机制

Binder是Android系统进程间通信(IPC)方式之一.Linux已经拥有的进程间通信IPC手段包括(Internet Process Connection): 管道(Pipe).信号(Signal)和跟踪(Trace).插口(Socket).报文队列(Message).共享内存(Share Memory)和信号量(Semaphore).本文详细介绍Binder作为Android主要IPC方式的优势. Binder机制概述: 基于Client-Server的通信方式广泛应用于从互联网和数据

Android多线程----异步消息处理机制之Handler详解

关于Android的多线程知识,请参考本人之前的一篇博客:Android 多线程----AsyncTask异步任务详解 在Android当中,提供了异步消息处理机制的两种方式来解决线程之间的通信问题,一种是今天要讲的Handler的机制,还有一种就是之前讲过的 AsyncTask 机制. 一.handler的引入: 我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃.相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟于心,即创

安卓自定义View进阶-Matrix详解

这应该是目前最详细的一篇讲解Matrix的中文文章了,在上一篇文章Matrix原理中,我们对Matrix做了一个简单的了解,偏向理论,在本文中则会详细的讲解Matrix的具体用法,以及与Matrix相关的一些实用技巧. Matrix方法表 按照惯例,先放方法表做概览. 方法类别 相关API 摘要 基本方法 equals hashCode toString toShortString 比较. 获取哈希值. 转换为字符串 数值操作 set reset setValues getValues 设置.

IPC之Posix信号量详解

基本概念: 信号量(semaphore)是一种用于提供不同进程间或一个给定进程的不用线程间同步手段的原语. 共有三种类型的信号量: 1)Posix有名信号量:使用Posix IPC名字标识,可用于进程或线程间的同步. 2)Posix基于内存的信号量:存放在共享内存区中,可用于进程或线程间的同步. 3)System V信号量:在内核中维护,可用于进程或者线程间同步.(本文不讨论System V信号量) 一个进程可以在某个信号量上执行三种操作: (1)创建(create)一个信号量.这还要求调用者指