写在前面:

本文摘自《Android开发艺术探索》。

什么是Binder?

Binder是Android中的一个类,实现了IBinder接口。

从IPC(进程间通信)角度讲,Binder是Android中的一种跨进程通信方式,Binder可以理解为一种虚拟的物理设备,它的驱动是/dev/binder,该通信方式在Linux中没有;

从Android Framework角度讲,Binder是ServiceManager连接各种Manger(ActivityManager、WindowManager,等等)和相应ManagerService的桥梁;

从Android 应用层将,Binder是客户端和服务端进行通信的媒介,当bindService时,服务器会返回一个包含服务业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。


什么时候使用Binder?

Binder主要使用在Service中,包括AIDL和Messenger(Messenger的底层其实是AIDL)。


什么是AIDL?

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

Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。Learn more>>

通俗地讲,AIDL为我们提供一种快速实现Binder的工具。(后续会说到)

AIDL文件代码如下:

// Book.aidlpackage com.cooffee.studypro.aidl;parcelable Book;
// IBookManager.aidlpackage com.cooffee.studypro.aidl;import com.cooffee.studypro.aidl.Book;interface IBookManager {    List<Book> getBookList();    void addBook(in Book book);}

系统自动帮我们生成IBookManager接口的java代码文件,代码结构如下:

interface IBookManager extends IInterface {            abstract class Stub implements IBookManager {            asInterface() {...};            asBinder() {...};            onTransact() {...};                        abstract class Proxy implements IBookManager {                asBinder() {...};                getInterfaceDescriptor() {...};                getBookList() {...};                addBook() {...};            }        }        getBookList();  // 抽象方法    addBook();      // 抽象方法        // 两个方法标记ID的声明...}


IBookManager接口的核心是它的内部类Stub和Stub的内部代理类Proxy。

下面详细介绍针对这两个类的每个方法的定义:

DESCRIPTOR:

Binder的唯一标识,一般用当前Binder的类名标识,比如“com.example.aidl.IBookManager”。

asInterface(android.os.IBinder obj):

将服务器端的Binder对象转换成客户端所需的AIDL接口类型的对象。

该转换过程是区分进程的:

如果客户端和服务端位于统一进程,此方法返回服务端的Stub对象本身;

否则返回的是系统封装的Stub.proxy对象。

asBinder():

用于返回当前Binder对象。

onTransact():

此方法运行在服务器端的Binder线程池,当客户端发起跨进程请求时,远程请求会通过系统低层封装后交由次方法来处理。

原型:

public Boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)。

code:服务器通过code,确定请求的目标方法是什么。

data:从data中取出目标方法所需的参数(如果目标方法有参数),然后执行目标方法。(感觉像:方法执行的入口)

reply:向reply写入返回值(如果请求的方法有返回值的话)。(感觉像:方法执行的出口)

注意:如果此方法返回false,那么客户端的请求会失败,因此我们利用这个特性来做权限验证,毕竟我们也不希望随便一个进程都能远程调用我们的服务。

Proxy#getBookList:

此方法运行在客户端,当客户端远程调用此方法时,其内部实现如下:

创建该方法的输入型Parcel对象_data、输出型Parcel对象_reply和返回值对象List;把该方法的参数信息写入_data(如果有参数);调用transact方法来发起RPC(远程过程调用)请求,同时当前线程挂起;服务器端的onTransact方法会被调用,直到RPC过程返回后,当前线程继续执行,并从_reply中取出RPC过程的返回结果;返回reply中的数据。

Proxy#addBook:

执行过程与getBookList过程相同,但是该方法没有返回值。


说明一下:

1. 当客户端发起远程请求时,由于当前线程会被挂起直至服务端进程返回数据,所以如果一个远程方法是耗时的,不能在UI线程发起此远程请求

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


IPC机制之Binder_第1张图片

Binder的工作机制

从上图我们可以看出,实现Binder的工作机制中并没有出现我们当初建立的AIDL文件,之所以提供AIDL文件,是为了方便系统为我们生成代码(IBookManager接口及其内部类)。


如果我们客户端远程请求工程中,中途断掉了怎么办?

Binder运行在服务端进程中,如果服务端进程由于某种原因异常终止,这个时候我们到服务端的Binder连接断裂(称之为Binder死亡),会导致我们的远程调用失败。而且我们不知道Binder连接是什么时候断裂的,那么客户端的功能会受到影响。

Binder提供了两个配对的方法linkToDeath和unlinkToDeath,通过linkToDeath我们可以为Binder设置一个死亡代理,当Binder死亡时,我们会收到通知,这个时候我们就可以重新发起连接请求从而恢复连接。


首先,声明一个DeathRecipient对象。DeathRecipient是一个接口,其内部只有一个方法binderDied,我们需要实现这个方法,当Binder死亡的时候,系统就会回调binderDied方法,然后我们就可以移出之前绑定的binder代理并重新绑定远程服务:

    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {        @Override        public void binderDied() {            if (mBookManager == null)                return;            mBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);            mBookManager - null;            // TODO: 重新绑定远程service        }    };

其次,在客户端绑定远程服务成功后,给binder设置死亡代理:

mService = IMessageBoxManager.Stub.asInterface(binder);binder.linkToDeath(mDeathRecipient, 0);

其中linkToDeath的第二个参数是个标记位,我们直接设为0即可。

另外,Binder的方法isBinderAlive也可以判断Binder是否死亡。


附录:

AIDL文件生成IBookManager接口代码

/* * This file is auto-generated.  DO NOT MODIFY. * Original file: /Volumes/J_Eric/J_workRoom/AndroidStudio/StudyPro/app/src/main/aidl/com/cooffee/studypro/aidl/IBookManager.aidl */package com.cooffee.studypro.aidl;public interface IBookManager extends android.os.IInterface{/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements com.cooffee.studypro.aidl.IBookManager{private static final java.lang.String DESCRIPTOR = "com.cooffee.studypro.aidl.IBookManager";/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR);}/** * Cast an IBinder object into an com.cooffee.studypro.aidl.IBookManager interface, * generating a proxy if needed. */public static com.cooffee.studypro.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.cooffee.studypro.aidl.IBookManager))) {return ((com.cooffee.studypro.aidl.IBookManager)iin);}return new com.cooffee.studypro.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.cooffee.studypro.aidl.Book> _result = this.getBookList();reply.writeNoException();reply.writeTypedList(_result);return true;}case TRANSACTION_addBook:{data.enforceInterface(DESCRIPTOR);com.cooffee.studypro.aidl.Book _arg0;if ((0!=data.readInt())) {_arg0 = com.cooffee.studypro.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.cooffee.studypro.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.cooffee.studypro.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.cooffee.studypro.aidl.Book> _result;try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);_reply.readException();_result = _reply.createTypedArrayList(com.cooffee.studypro.aidl.Book.CREATOR);}finally {_reply.recycle();_data.recycle();}return _result;}@Override public void addBook(com.cooffee.studypro.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.cooffee.studypro.aidl.Book> getBookList() throws android.os.RemoteException;public void addBook(com.cooffee.studypro.aidl.Book book) throws android.os.RemoteException;}


更多相关文章

  1. 解决EventBus中接收方法中无法更新UI的问题
  2. Android .so abi兼容,通用armeabi-v7a和arm64-v8a架构的方法
  3. Android 首选网络模式默认值的修改方法
  4. Android学习笔记-Android非布局activity中布局文件及控件加载方
  5. Android基于OpenGL的GLSurfaceView创建一个Activity实现方法
  6. 【转】Android-Action Bar使用方法
  7. 在Android上实现SSL握手(客户端需要密钥和证书),实现服务器和客户端

随机推荐

  1. Android中的动画有哪几类?各自的特点和区
  2. 终于松了口气,说说这周我调试Sate210 andr
  3. Android(安卓)Material Design 详解(使用s
  4. 移动端开发新趋势Flutter
  5. Android(安卓)Layout 之TableLayout
  6. 绝对让你理解Android中的Context
  7. 一些 Android(安卓)小知识点的总结
  8. Android(安卓)Handler使用的安全问题
  9. android 最简单的圆角阴影效果
  10. eclipse下android工程目录讲解