IPC机制之Binder

写在前面:

本文摘自《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.aidl
package com.cooffee.studypro.aidl;

parcelable Book;
// IBookManager.aidl
package 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方法不管是否耗时都应该采用同步的方式实现,因为它已经运行在一个线程中了。



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;
}


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值