Binder机制浅析

本文详细解析了Android中的Binder机制,介绍了其在进程间通信中的作用、实现原理及使用方法。通过AIDL示例深入理解Binder的工作流程。

关于Binder机制,看了2天还是“糊里糊涂”的,该总结自己理解的Binder了。纯属自己总结,不对的地方,请多多指正。

Binder机制有啥用

Binder机制主要解决的是IPC(Inner Process Communicate),进程间通信问题。不同进程管理者自己的“东西”(内存),有属性,方法之类的,如果进程A想访问进程B的“东西”,只能通过IPC实现。
IPC的形式有很多种,如共享内存,管道,socket,文件等等,至于Android为什么还要自己设计一套专门的机制去实现IPC,主要考虑2方面:(1)性能(只拷贝一次)。(2)安全。

Binder机制的4个角色

(1)Client: 客户端,可以是任意一个进程,是远程(服务)请求的发起者。
(2)Server: 服务端,可以是任意一个进程,主要提供服务,请求的执行者。
(3)ServiceManager: Server需要在这里注册自己,提供自己的信息,Client可以在这里获取Server的引用。
(4)Binder驱动: Server和Client通信的媒介。

Binder机制实现IPC的流程

  1. 新建Server后,需要在ServiceManager注册,提供自己的信息。ServiceManager会根据Server的信息,生成IBinder引用,相当于Server的“引用“。

  2. Client向ServiceManager查询目标Server,并获取目标Server的IBinder引用。有没有发现,Client向ServiceManager查询Server,也是一个IPC的过程,此时ServiceManager也是一个Server。由于ServiceManger的IBinder是固定的,而且别所有人知道的,所以Client还是很容易获取ServiceManager的IBinder,从而访问ServiceManager。ServiceManager会返回目标Server的IBinder对象,此时Client就可以与Server“通信“了。

  3. Client发出请求,调用Server的方法,此时Client的线程会挂起,Binder驱动会把Client要传送的数据从Client的用户空间复制到Server内核空间的内存中,因为Server内核空间的内存已经通过mmap映射到Server的用户空间中,Binder驱动只需要将刚才复制之后的数据所在的内核空间的地址告诉Server就行,这样Server就可以获取Client传送过来的数据了。这就是传说中,一次拷贝。相反,待Server执行完请求的方法后,Binder驱动通过相同的方法,将Server的结果数据从Server的用户空间复制到Client的内核空间中,并将地址告诉Client即可。因为Client请求时,线程被挂起,现在结果已经有了,Binder驱动唤起Client线程,通知Client请求的方法已经执行完了,你可以继续干你的活了。

Binder机制的使用:AIDL(Android Interface Define Language)

我们从使用的代码进一步了解Binder机制。两个不同进程通信,需要统一“语言”,需要做一定的规范,要不然进程A怎么知道进程B有什么呢。所以就有了AIDL。
我们先创建一个有计算功能的Server,提供Client计算用。
创建一个Calculator.aidl,代码如下:

package com.johan.aidl;
interface Calculator {
    int add(int num1, int num2);
    int sub(int num1, int num2);
}

eclipse工具就会调用aidl.exe自动帮我们生成对应的名为Calculator的java文件(在gen目录下),代码如下:

package com.johan.aidl;

public interface Calculator extends android.os.IInterface {
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.johan.aidl.Calculator {
        private static final java.lang.String DESCRIPTOR = "com.johan.aidl.Calculator";

        /** Construct the stub at attach it to the interface. */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.johan.aidl.Calculator interface,
         * generating a proxy if needed.
         */
        public static com.johan.aidl.Calculator asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.johan.aidl.Calculator))) {
                return ((com.johan.aidl.Calculator) iin);
            }
            return new com.johan.aidl.Calculator.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_add: {
                data.enforceInterface(DESCRIPTOR);
                int _arg0;
                _arg0 = data.readInt();
                int _arg1;
                _arg1 = data.readInt();
                int _result = this.add(_arg0, _arg1);
                reply.writeNoException();
                reply.writeInt(_result);
                return true;
            }
            case TRANSACTION_sub: {
                data.enforceInterface(DESCRIPTOR);
                int _arg0;
                _arg0 = data.readInt();
                int _arg1;
                _arg1 = data.readInt();
                int _result = this.sub(_arg0, _arg1);
                reply.writeNoException();
                reply.writeInt(_result);
                return true;
            }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.johan.aidl.Calculator {
            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 int add(int num1, int num2) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(num1);
                    _data.writeInt(num2);
                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public int sub(int num1, int num2) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(num1);
                    _data.writeInt(num2);
                    mRemote.transact(Stub.TRANSACTION_sub, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_sub = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public int add(int num1, int num2) throws android.os.RemoteException;
    public int sub(int num1, int num2) throws android.os.RemoteException;
}

我们开始解析eclipse帮我们生成的类。首先是这个类是一个继承IInterface 接口,don’t care。这个接口定义了两个内部类,Stub和Proxy。
我们先看看Stub(存根)这个类,这个类是一个抽象类,继承Binder,证明他就是一个Binder,我们写一个服务类时,继承Service,当客户端bindService时,返回的就是这个Binder的子类。还实现了我们定义的aidl接口,交给子类去实现。我们看他的构造函数:

private static final java.lang.String DESCRIPTOR = "com.johan.aidl.Calculator";
public Stub() {
    this.attachInterface(this, DESCRIPTOR);
}

构造函数里的attachInterface的作用是保存自己的描述和引用,下面会有用到,继续看看asInterface这个方法:

public static com.johan.aidl.Calculator asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.johan.aidl.Calculator))) {
        return ((com.johan.aidl.Calculator) iin);
    }
    return new com.johan.aidl.Calculator.Stub.Proxy(obj);
}

obj.queryLocalInterface(DESCRIPTOR)根据描述查看本地是否有目标服务类,如果想调用的服务正好和自己在同一个进程,直接返回找到的服务类,如果没有找到,说明目标服务类和自己不在同一个进程,则返回Proxy代理类。这下我们得好好看看这个代理类,单独拿出来:

private static class Proxy implements com.johan.aidl.Calculator {
    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 int add(int num1, int num2) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        int _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            _data.writeInt(num1);
            _data.writeInt(num2);
            mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
            _reply.readException();
            _result = _reply.readInt();
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }
    @Override
    public int sub(int num1, int num2) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        int _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            _data.writeInt(num1);
            _data.writeInt(num2);
            mRemote.transact(Stub.TRANSACTION_sub, _data, _reply, 0);
            _reply.readException();
            _result = _reply.readInt();
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }
}

代理类持有服务类的IBinder引用,也实现了我们定义的aidl接口,所以他“算是”有服务类功能。一般客户端都会获取到的服务类,都是这个代理类。我们调用这个代理服务类的方法,如add方法,代码:

@Override
public int add(int num1, int num2) throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    int _result;
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        _data.writeInt(num1);
        _data.writeInt(num2);
        mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
        _reply.readException();
        _result = _reply.readInt();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;
}

会先从池中取出两个Parcel对象存放传送的数据和返回的结果,然后调用IBinder的transact方法,此时,客户线程会挂起。通过Binder驱动,服务进程的Stub(真正的服务类)会调用onTransact方法。IBinder调用transact方法时,传入Stub.TRANSACTION_add值,这就指明要服务类调用add方法,参数通过_data传进去,服务类执行完目标方法后,会把结果写入_reply,此时,Binder驱动会唤醒客户线程,代理服务类通过读取_reply数据返回给客户。

static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_sub = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

AIDL为我们接口每一个方法定义了一个标志,好让服务类知道客户请求的是哪个方法。AIDL算是分析完了,如果可以的话,建议结合例子会更加深入。

AIDL例子代码

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值