一、概述
-
Binder是什么?
- 从功能上讲:Binder是一种在Android设备上进行IPC(Inter-Process Communication,进程间通信)的主要方式,主要用来实现跨进程通信。
- 从代码上讲:Binder是一个类,实现了IBinder接口。
- 从Android Framwork角度来说,Binder是ServerManager连接各种Manager和ManagerService的桥梁。
-
为什么需要Binder?
- 进程隔离:为了保证安全性&独立性,一个进程不能直接操作或者访问另一个进程,即Android的进程是相互独立、隔离的
- IPC(跨进程通信):在实际开发中,经常需要进程间的合作、交互和通信
- 进程空间的分配:
用户空间:数据不可共享,即不可共享空间
内核空间:数据可以共享,即空闲空间
- 通过上图可以看到,各个进程的用户空间是无法共享的,为了实现进程间的通信,则需要内核空间作为中介来进行数据的交换。如下图:
工作流程: 发送进程通过系统调用,将需要发送的数据拷贝到内核空间的缓冲区中。内核服务程序唤醒接收进程的接收线程,通过系统调用将数据发送到接收进程的用户空间中,完成数据的发送。所以这里通过两次拷贝完成了一次数据的发送。
-
Binder IPC模型
-
原理图
Binder跨进程通信机制模型是基于Cilent-Server模式,即cilent进程-server进程。
说明:Client进程、Server进程、Service Manager之间的数据交互都需要通过Binder来完成。且Service Manager和Binder驱动都是Android基础架构已经帮写好了的,开发人员只需要创建Client进程和Server进程即可。
-
模型相关角色
作用 备注 Client进程 使用服务的进程 Server进程 提供服务的进程 ServerManager进程 管理Service的注册和查询 Binder驱动 一种虚拟设备驱动,是链接Service进程、Client进程、Service Manager的桥梁。作用为:1.通过内存映射来传递进程间的数据 2.实现线程控制,采用Binder的线程池,并由Binder驱动自身进行管理 -
跨进程通信的基本原理
核心原理:内存映射
工作流程:- Binder驱动创建一块接收缓冲区
- 在内核空间中确定一块用于接收数据的Buffer,然后将用户空间的Buffer与内核空间的Buffer映射到实际的物理内存上,实现用户空间Buffer与内核空间Buffer的同步化
- 由于内核缓冲区&接收进程的用户地址空间存在映射关系,故相当于也发送到了接收进程的用户地址空间,实现了跨进程通信
-
二、根据代码进行分析Binder的工作过程
Android开发中,Binder主要用在Serviec中,包括AIDL和Messenger,其中普通Service中的Binder不涉及进程间通信,无法触及Binder的核心,而Message的底层其实是AIDL,所以可以用AIDL来分析Binder的工作机制。
-
创建Book.java Book.aidl IBookManager.aidl 三个文件
Book.java 实现Parcelable接口
package com.kanghui.demo.aidl; import android.os.Parcel; import android.os.Parcelable; public class Book implements Parcelable { public String bookName; public int bookId; public Book(int bookId, String bookName) { this.bookId = bookId; this.bookName = bookName; } protected Book(Parcel in) { bookName = in.readString(); bookId = in.readInt(); } public static final Creator<Book> CREATOR = new Creator<Book>() { @Override public Book createFromParcel(Parcel in) { return new Book(in); } @Override public Book[] newArray(int size) { return new Book[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(bookName); dest.writeInt(bookId); } }
Book.aidl 是Book类在AIDL中的声明
// Book.aidl package com.kanghui.demo.aidl; // Declare any non-default types here with import statements parcelable Book;
IBookManager.aidl 对外提供获取书籍和添加书籍的方法
// IBookManager.aidl package com.kanghui.demo.aidl; import com.kanghui.demo.aidl.Book; // Declare any non-default types here with import statements interface IBookManager { List<Book> getBookList(); void addBook(in Book book); /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); }
通过上面写的相关类,系统会为IBookManager 生成Binder类,在build-generate 目录下生成了IBookManager.java的类
/* * This file is auto-generated. DO NOT MODIFY. */ package com.kanghui.demo.aidl; // Declare any non-default types here with import statements public interface IBookManager extends android.os.IInterface { /** Default implementation for IBookManager. */ public static class Default implements com.kanghui.demo.aidl.IBookManager { @Override public java.util.List<com.kanghui.demo.aidl.Book> getBookList() throws android.os.RemoteException { return null; } @Override public void addBook(com.kanghui.demo.aidl.Book book) throws android.os.RemoteException /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException { } @Override public android.os.IBinder asBinder() { return null; } } /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.kanghui.demo.aidl.IBookManager { private static final java.lang.String DESCRIPTOR = "com.kanghui.demo.aidl.IBookManager"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.kanghui.demo.aidl.IBookManager interface, * generating a proxy if needed. */ public static com.kanghui.demo.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.kanghui.demo.aidl.IBookManager))) { return ((com.kanghui.demo.aidl.IBookManager)iin); } return new com.kanghui.demo.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 { java.lang.String descriptor = DESCRIPTOR; switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(descriptor); return true; } case TRANSACTION_getBookList: { data.enforceInterface(descriptor); java.util.List<com.kanghui.demo.aidl.Book> _result = this.getBookList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addBook: { data.enforceInterface(descriptor); com.kanghui.demo.aidl.Book _arg0; if ((0!=data.readInt())) { _arg0 = com.kanghui.demo.aidl.Book.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addBook(_arg0); reply.writeNoException(); return true; } case TRANSACTION_basicTypes: { data.enforceInterface(descriptor); int _arg0; _arg0 = data.readInt(); long _arg1; _arg1 = data.readLong(); boolean _arg2; _arg2 = (0!=data.readInt()); float _arg3; _arg3 = data.readFloat(); double _arg4; _arg4 = data.readDouble(); java.lang.String _arg5; _arg5 = data.readString(); this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5); reply.writeNoException(); return true; } default: { return super.onTransact(code, data, reply, flags); } } } private static class Proxy implements com.kanghui.demo.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.kanghui.demo.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.kanghui.demo.aidl.Book> _result; try { _data.writeInterfaceToken(DESCRIPTOR); boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0); if (!_status && getDefaultImpl() != null) { return getDefaultImpl().getBookList(); } _reply.readException(); _result = _reply.createTypedArrayList(com.kanghui.demo.aidl.Book.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addBook(com.kanghui.demo.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); } boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0); if (!_status && getDefaultImpl() != null) { getDefaultImpl().addBook(book); return; } _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) 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.writeInt(anInt); _data.writeLong(aLong); _data.writeInt(((aBoolean)?(1):(0))); _data.writeFloat(aFloat); _data.writeDouble(aDouble); _data.writeString(aString); boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0); if (!_status && getDefaultImpl() != null) { getDefaultImpl().basicTypes(anInt, aLong, aBoolean, aFloat, aDouble, aString); return; } _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } public static com.kanghui.demo.aidl.IBookManager sDefaultImpl; } static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); public static boolean setDefaultImpl(com.kanghui.demo.aidl.IBookManager impl) { if (Stub.Proxy.sDefaultImpl == null && impl != null) { Stub.Proxy.sDefaultImpl = impl; return true; } return false; } public static com.kanghui.demo.aidl.IBookManager getDefaultImpl() { return Stub.Proxy.sDefaultImpl; } } public java.util.List<com.kanghui.demo.aidl.Book> getBookList() throws android.os.RemoteException; public void addBook(com.kanghui.demo.aidl.Book book) throws android.os.RemoteException; /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException; }
这个类IBookManager.java继承了IInterface,它本身也是一个接口。接口本身申明了两个方法:getBookList()和addBook。同时申明了两个ID标识这两个方法,这两个id用于表示在transact过程中客户端到底请求的是哪个方法。
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
其次,申请了一个内部类Stub继承自Binder,当客户端和服务端都位于同一个进程时,方法调用不会走跨进程的transact过程,而当两者位于不同进程是,方法调用需要走transct过程。这个逻辑由Stub的内部代理类Proxy来完成。
主要方法介绍:
法 介绍 DESCRIPTOR Binder的唯一标识,一般用当前的Binder的类名表示,如com.kanghui.demo.aidl.IBookManager asInterface(android.os.IBinder obj) 将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象。 asBinder 用于返回当前的Binder对象 onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) 这个方法运行在服务端的Binder线程池中,当客户端发起跨进程请求时,服务端根据code确定目标方法,从data参数中取出所需参数,执行完后向apply写入返回值。