第 2 章 IPC 机制

2.2 IPC 基础概念 Binder

前言:Binder 真的很复杂,我现在也没有十分的理解,不过,我觉得随着编程时间的增加,会慢慢的理解的,就像刚学会编程,去看设计模式,是看不太懂的,随着开发时间的增加,开发中遇到一些需求改变因此修改代码的情况,发现修改起代码来会比较繁琐,这个时候,接触一些设计就会发现,会品尝到设计模式的美味,也就进而理解设计模式了。

Binder 简单介绍

直观来说,Binder 是 Android 中的一个类,它实现了 IBinder 接口。从 IPC 角度来说,Binder 是 Android 中的一种跨进程通信方式,Binder 还可以理解为一种虚拟的物理设备,它的设备驱动是 /dev/binder,该通信方式在 Linux 中没有;从 Android Framework 角度来说,Binder 是 ServiceManager 连接各种 Manager(ActivityManager、WindowManager,等等)和相应 ManagerService 的桥梁;从 Android 应用层来说,Binder 是客户端和服务端进行通信的媒介,当 bindService 的时候,服务端会返回一个包含了服务端业务调用的 Binder 对象,通过这个 Binder 对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于 AIDL 的服务。

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

Book.java

package com.will.test;

import android.os.Parcel;
import android.os.Parcelable;

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

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


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

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

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

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

Book.aidl

// Book.aidl
package com.will.test;

// Declare any non-default types here with import statements

parcelable Book;

IBookManager.aidl

package com.will.test;

import com.will.test.Book;

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

上面是一个简单的 AIDL 服务端的代码,我们点击编译按钮,系统会为我们生成一个 Binder 类。

IBookManager.java

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/wangzhenhua/Documents/AndroidProject/CustomizeStack/app/src/main/aidl/com/will/test/IBookManager.aidl
 */
package com.will.test;
public interface IBookManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.will.test.IBookManager
{
private static final java.lang.String DESCRIPTOR = "com.will.test.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.will.test.IBookManager interface,
 * generating a proxy if needed.
 */
public static com.will.test.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.will.test.IBookManager))) {
return ((com.will.test.IBookManager)iin);
}
return new com.will.test.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.will.test.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(descriptor);
com.will.test.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.will.test.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.will.test.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.will.test.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.will.test.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.will.test.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.will.test.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.will.test.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.will.test.Book book) throws android.os.RemoteException;
}

接下来进行一个简单的分析,虽然分析看起来也不懂,这个就像是前面提到的设计模式一样,随着编程时间的推移自然而然的就会知道了。

IBookManager.java 这个类继承了 IInterface 这个接口,同时它自己也是一个接口,所有可以在 Binder 中传输的接口都需要继承 IInterface 接口。

这个类中,首先它声明了两个方法 getBookList 和 addBook,显然这就是我们在 IBookManager.aidl 中所声明的方法,它还声明了两个整形的 id 分别用于标识这两个方法,这两个 id 用于标识在 transact 过程中客户端所请求的到底是哪个方法。

它声明了一个 Stub 内部类,这个 Stub 是一个 Binder 类,当客户端和服务端都位于同一个进程时,方法调用不会走跨进程的 transact 过程,当两者位于不同进程时,方法调用需要走 transact 过程,这个逻辑由 Stub 的内部代理类 Proxy 来完成。

DESCRIPTOR

Binder 的唯一标识,一般用当前 Binder 的类名表示,比如本例的“com.will.test.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 可以确定客户端所请求的目标方法是什么,从 data 中取出目标方法所需的参数(如果目标方法有参数的话),然后执行目标方法。当目标方法执行完毕后,就向 reply 中写入返回值(如果目标有返回值的话)。需要注意的是,如果此方法返回 false,那么客户端的请求会失败,因此我们可以利用这个特性来做权限验证。

Proxy#getBookList

这个方法运行在客户端,当客户端远程调用此方法时,它的内部实现是这样的:首先创建该方法所需要的输入型 Parcel 对象 _data、输出型 Parcel 对象 _reply 和返回值对象 List;然后把该方法的参数信息写入 _data 中(如果有参数的话);接着调用 transact 方法来发起 RPC (远程过程调用)请求,同时当前线程挂起;然后服务端的 onTransact 方法会被调用,直到 RPC 过程返回后,当前线程继续执行,并从 _reply 中取出 RPC 过程的返回结果;最后返回 _reply 中的数据。

Proxy#addBook

和 getBookList 是一样的,addBook 没有返回值,不需要从 _reply 中取出返回值。

最后,当客户端发起远程请求时,由于当前线程会被挂起直至服务端进程返回数据,所以如果一个远程方法是很耗时的,那么不能在 UI 线程中发起此远程请求;其次,由于服务端的 Binder 方法运行在 Binder 的线程池中,所以 Binder 方法不管是否耗时都应该采用同步的方式去实现,因为它已经运行在一个线程中了。

其实我们可以手动实现一个 Binder,不依赖 AIDL 文件,AIDL 文件的本质是系统为我们提供了一种快速实现 Binder 的工具,仅此而已。

我们知道 Binder 运行在服务端进程,如果服务端进程由于某种原因异常终止,我们的 Binder 连接断裂(Binder 死亡),会导致我们的远程调用失败。如何解决这个问题呢?Binder 中提供了两个配对的方法 linkToDeath 和 unlinkToDeath,通过 linkToDeath 我们可以给 Binder 设置一个死亡代理,当 Binder 死亡时,我们就会收到通知,这个时候我们可以重新发起连接请求。

首先声明一个 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 这里重新绑定远程服务
        }
    };

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

mService = IBookManager.Stub.asInterface(binder);
        binder.linkToDeath(mDeathRecipient, 0);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值