Binder Java 层实现原理

近日在做组件化方案时,复习了一遍 Bidner 机制,在熟悉了一遍 Bidner 机制后,对进程间通讯以及 Android 设计模式原来有了较深的感悟。 Android Binder 是一个及其深入的话题,从 Linux 间进程通信的方式,到 Android 间通信方式都需要了解,下图是 binder 大致实现(看完我就晕了)。本文不通过复杂的代码细节,以及底层代码分析 Binder 实现方式。而是通过相对好理解的 AIDL(也是大众认为的最好理解 binder 的方式)来熟悉 binder。对 AIDL 不了解的朋友,可先行看下三步掌握 Android 中的 AIDL来大致了解一下期使用方式。

复杂的binder

Binder 介绍

这里我们不讲述上面复杂的实现。而是通过 AIDL 介绍 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 的服务。

下面是通过 Aidl 实现进程间通信的简化图,您可以不记住,这里有个印象就好。

创建 AIDL

我们先创建三个文件分别为 Market.java、 Market.aidl、ImarketManger.aidl 文件 代码如下:

Market.java

public class Market implements Parcelable {
    private int goodsId;
    private String goodsName;
public Market(int goodsId, String goodsName) {
    this.goodsId = goodsId;
    this.goodsName = goodsName;
}


protected Market(Parcel in) {
    goodsId = in.readInt();
    goodsName = in.readString();
}

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

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

public static final Creator<Market> CREATOR = new Creator<Market>() {
    @Override
    public Market createFromParcel(Parcel in) {
        return new Market(in);
    }

    @Override
    public Market[] newArray(int size) {
        return new Market[size];
    }
};

}

复制代码

Market.aidl

// Market.aidl
package com.ccdt.itvision.demo;

parcelable Market;

复制代码

ImarketManger.aidl

// IMarketManger.aidl
package com.ccdt.itvision.demo;
import com.ccdt.itvision.demo.Market;
// Declare any non-default types here with import statements

interface IMarketManger {

List<Market> getGoodList();

void addGoods(in Market market);
}

复制代码

上面三个文件中,Market 表示一个超市食物类,它实现了 Parcelable 接口。Market.aidl 是 Market 类在 AIDL 中的声明。IMarketManger.aidl 是我们自己定义的一个接口。里面有两个方法。然后我们rebuild project会在 app/build/generated/source/com.xxx.xxx/ 看见 ImarketManger.java 文件这个文件就是我们研究的重中之重!它的全部代码如下:

public interface IMarketManger extends android.os.IInterface {
/**
 * Local-side IPC implementation stub class.
 */
public static abstract class Stub extends android.os.Binder implements com.ccdt.itvision.viewlearning.IMarketManger {
    private static final java.lang.String DESCRIPTOR = "com.ccdt.itvision.demo.IMarketManger";
/**
 * Construct the stub at attach it to the interface.
 */
public Stub() {
    this.attachInterface(this, DESCRIPTOR);
}

/**
 * Cast an IBinder object into an com.ccdt.itvision.viewlearning.IMarketManger interface,
 * generating a proxy if needed.
 */
public static com.ccdt.itvision.viewlearning.IMarketManger asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) &amp;&amp; (iin instanceof com.ccdt.itvision.viewlearning.IMarketManger))) {
        return ((com.ccdt.itvision.viewlearning.IMarketManger) iin);
    }
    return new com.ccdt.itvision.viewlearning.IMarketManger.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_getGoodList: {
            data.enforceInterface(DESCRIPTOR);
            java.util.List&lt;com.ccdt.itvision.viewlearning.Market&gt; _result = this.getGoodList();
            reply.writeNoException();
            reply.writeTypedList(_result);
            return true;
        }
        case TRANSACTION_addGoods: {
            data.enforceInterface(DESCRIPTOR);
            com.ccdt.itvision.viewlearning.Market _arg0;
            if ((0 != data.readInt())) {
                _arg0 = com.ccdt.itvision.demo.Market.CREATOR.createFromParcel(data);
            } else {
                _arg0 = null;
            }
            this.addGoods(_arg0);
            reply.writeNoException();
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}

private static class Proxy implements com.ccdt.itvision.viewlearning.IMarketManger {
    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&lt;com.ccdt.itvision.viewlearning.Market&gt; getGoodList() throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.util.List&lt;com.ccdt.itvision.viewlearning.Market&gt; _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(Stub.TRANSACTION_getGoodList, _data, _reply, 0);
            _reply.readException();
            _result = _reply.createTypedArrayList(com.ccdt.itvision.viewlearning.Market.CREATOR);
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

    @Override
    public void addGoods(com.ccdt.itvision.viewlearning.Market market) 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 ((market != null)) {
                _data.writeInt(1);
                market.writeToParcel(_data, 0);
            } else {
                _data.writeInt(0);
            }
            mRemote.transact(Stub.TRANSACTION_addGoods, _data, _reply, 0);
            _reply.readException();
        } finally {
            _reply.recycle();
            _data.recycle();
        }
    }
}

static final int TRANSACTION_getGoodList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addGoods = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

}

public java.util.List<com.ccdt.itvision.viewlearning.Market> getGoodList() throws android.os.RemoteException;

public void addGoods(com.ccdt.itvision.viewlearning.Market market) throws android.os.RemoteException;
}

复制代码

看起来很复杂,但其实这个类逻辑还是蛮清晰。我们缩进下代码在看。

首先这里声明了两个方法, getGoodList()addGoods() 。这两个方法显然是我们在 ImarketManger 中声明的。然后是一个静态的内部类 Stub。stub 继承自 Binder,显然他自己就是一个 Binder 类。当客户端和服务端都位于同一个进程时,方法调用不会走跨进程的 transact 过程,而当两者位于不同进程时,方法调用需要走 transact 过程,这个逻辑由 Stub 的内部代理类 Proxy 来完成。所以这里的核心就是 Stub 的内部代理 Proxy。Proxy 内部方法如下:

  • asInterface(android.os.IBinder obj)

用于将服务端的 Binder 对象转换成客户端所需的 AIDL 接口类型的对象,这种转换过程是区分进程的,如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的 Stub 对象本身,否则返回的是系统封装后的 Stub.proxy 对象。

  • asBinder

此方法用于返回当前 Binder 对象

  • onTransact

此方法运行在服务端中的 Binder 线程池中,当客户端通过 aidl 发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。服务端通过 code 可以确定客户端所请求的方法是什么,接着从 data 中取出该方法所需的参数(没参数则不取),然后执行该方法。当该方法执行完毕后,就向 reply 中写入返回值,这就是 onTransact 的执行过程。值得注意的是,如果此方法返回 false,那么客户端的请求会失败

  • Proxy#getGoodsList

首先创建该方法所需要的输入型 Parcel 对象 _data、输出型 Parcel 对象 _reply。 声明返回值对象 List。 _data.writeInterfaceToken(DESCRIPTOR); 将该方法所需参数信息写入 data 中。 接着调用 transact 方法来发起 RPC(远程过程调用)请求,当前线程 hang on(挂起),当服务端 onTransact(刚介绍完的方法,回头去看一看)执行完毕后。从 _reply 中取出 RPC 过程的返回结果处理完毕。

通过以上分析,下面我们在回头看刚刚的简图是不是就明朗了许多?

手写一个 AIDL 的实现类

从上述分析过程来看,我们完全可以不提供 AIDL 文件即可实现 Binder,之所以提供 AIDL 文件,是为了方便系统为我们生成代码。系统根据 AIDL 文件生成 Java 文件的格式是固定的,我们可以不用 AIDL 文件直接写一个 Binder 出来。

首先声明一个接口继承 IInterface 代码如下:

public interface IMarketManger extends IInterface {
    static final String DESCRIPTOR = "com.ccdt.itvision.demo.custombinder.IMarketManger";
    static final int TRANSACTION_getGoodList = IBinder.FIRST_CALL_TRANSACTION + 0;
    static final int TRANSACTION_addGood = IBinder.FIRST_CALL_TRANSACTION+1;
    public List<Market> getGoodList() throws RemoteException;
    public void addGoods(Market market) throws RemoteException;
}
复制代码

接着,创建 ImarketManger 的实现类,实现思路与 AIDL 生成的代码类似。如果服务端使用手写 Binder,只需要在 onBinder 的时候返回 ImarketManger 的实现类就好。代码如下:

public class MarketImpl extends Binder implements IMarketManger {

public MarketImpl() {
this.attachInterface(this, DESCRIPTOR);
}

public static IMarketManger asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IInterface iInterface = obj.queryLocalInterface(DESCRIPTOR);
if (iInterface != null && iInterface instanceof IMarketManger) {

    return (IMarketManger) iInterface;
}
return new MarketImpl.Proxy(obj);

}

@Override
public List<Market> getGoodList() throws RemoteException {
return null;
}

@Override
public void addGoods(Market good) throws RemoteException {

}

@Override
public IBinder asBinder() {
return this;
}

@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION:
reply.writeString(DESCRIPTOR);
return true;

    case TRANSACTION_getGoodList:
        data.enforceInterface(DESCRIPTOR);
        List&lt;Market&gt; goodList = this.getGoodList();
        reply.writeNoException();
        reply.writeTypedList(goodList);
        return true;

    default:
    
        break;
}
return super.onTransact(code, data, reply, flags);

}

private static class Proxy implements IMarketManger {
private IBinder mRemote;

public Proxy(IBinder obj) {
    mRemote = obj;
}

@Override
public List&lt;Market&gt; getGoodList() throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    List&lt;Market&gt; result;
    try {
        data.writeInterfaceToken(DESCRIPTOR);
        mRemote.transact(TRANSACTION_getGoodList, data, reply, 0);
        reply.readException();
        result = reply.createTypedArrayList(Market.CREATOR);
    } finally {
        reply.recycle();
        data.recycle();
    }
    return result;
}

@Override
public void addGoods(Market market) throws RemoteException {
    // TODO: 与aidl中一样
}


@Override
public IBinder asBinder() {
    return mRemote;
}

}

复制代码

Aidl 只是 Google 为我们实现 Binder 实现的一个简便方式生成模板化代码,事实上我们完全可以这样手写一个 aidl,这有利于我们理解 Bidner 的实现方式。另外 Binder 还有个重要的方法,Linktodeath() 因服务端的情况我们未知,可能出现异常情况,当 binder 死亡时,我们可以通过 binder 死亡代理 知道链接已经断开,重新绑定链接。

最后 Binder 的实现机制看似复杂,但是其中很有条理,如果你想使用组件化方案,了解 android 底层原理,做 framwork 层代码,binder 使我们绕不开的话题,所以先在 Aidl 开始了解并深入,对您的成长是非常有意义的!

  • Android

    开放手机联盟(一个由 30 多家科技公司和手机公司组成的团体)已开发出 Android,Android 是第一个完整、开放、免费的手机平台。

    293 引用
感谢    赞同    分享    收藏    关注    反对    举报    ...