Binder机制浅析

来源:互联网 发布:淘宝店怎么加入天猫 编辑:程序博客网 时间:2024/06/14 13:56

关于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方法,代码:

@Overridepublic 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例子代码

原创粉丝点击