从AIDL来认识Binder

来源:互联网 发布:http默认端口号8080 编辑:程序博客网 时间:2024/05/24 22:44

在平时的Android开发过程中,很少接触到Binder,对这个东西也是带了几分畏惧,虽然知道这个很重要,但是,理解起来却不是一件容易的事情。今天就带大家从AIDL来分析Binder。 
Binder是一个C/S架构,就是说分为客户端跟服务端。这里的客户端跟服务端不是我们平时开发中所说的那样,这里的客户端是指发送消息的一端,服务端是指接受消息的一端。 
Binder在进程间通信发挥了很大的作用,起到了一个媒介的作用,谁给谁发送或者接受消息都要通过它。这里的进程间通信不只是单纯的值多个进程之间,一个进程里的通信也是通过Binder,像四大组件的通信,这些都是通过Binder的。 
相信大家也或多或少的使用过AIDL来实现进程间通信,在Androidstudio中,可以通过新建一个AIDL文件,然后,编译器自动帮我们生成这样一个文件,不需要我们手动再去编写,提供了很大的便利。下面我们来看下编译器帮我们生成的AIDL文件长什么样子。

public interface IMyAidlInterface extends IInterface {    void basicTypes(int var1, long var2, boolean var4, float var5, double var6, String var8) throws RemoteException;    public abstract static class Stub extends Binder implements IMyAidlInterface {        private static final String DESCRIPTOR = "com.rxdemo.IMyAidlInterface";        static final int TRANSACTION_basicTypes = 1;        public Stub() {            this.attachInterface(this, "com.rxdemo.IMyAidlInterface");        }        public static IMyAidlInterface asInterface(IBinder obj) {            if(obj == null) {                return null;            } else {                IInterface iin = obj.queryLocalInterface("com.rxdemo.IMyAidlInterface");                return (IMyAidlInterface)(iin != null && iin instanceof IMyAidlInterface?(IMyAidlInterface)iin:new IMyAidlInterface.Stub.Proxy(obj));            }        }        public IBinder asBinder() {            return this;        }        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {            switch(code) {            case 1:                data.enforceInterface("com.rxdemo.IMyAidlInterface");                int _arg0 = data.readInt();                long _arg1 = data.readLong();                boolean _arg2 = 0 != data.readInt();                float _arg3 = data.readFloat();                double _arg4 = data.readDouble();                String _arg5 = data.readString();                this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);                reply.writeNoException();                return true;            case 1598968902:                reply.writeString("com.rxdemo.IMyAidlInterface");                return true;            default:                return super.onTransact(code, data, reply, flags);            }        }        private static class Proxy implements IMyAidlInterface {            private IBinder mRemote;            Proxy(IBinder remote) {                this.mRemote = remote;            }            public IBinder asBinder() {                return this.mRemote;            }            public String getInterfaceDescriptor() {                return "com.rxdemo.IMyAidlInterface";            }            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {                Parcel _data = Parcel.obtain();                Parcel _reply = Parcel.obtain();                try {                    _data.writeInterfaceToken("com.rxdemo.IMyAidlInterface");                    _data.writeInt(anInt);                    _data.writeLong(aLong);                    _data.writeInt(aBoolean?1:0);                    _data.writeFloat(aFloat);                    _data.writeDouble(aDouble);                    _data.writeString(aString);                    this.mRemote.transact(1, _data, _reply, 0);                    _reply.readException();                } finally {                    _reply.recycle();                    _data.recycle();                }            }        }    }}
首先是要继承自IInterface,这是肯定的,编写AIDL时都需要继承这个接口。

我们可以看到这里生成了两个内部类,Stub 跟 Proxy  ,Stub 继承自Binder 并且实现了IMyAidlInterface, 可以看出这是一个Binder, Proxy 实现了  IMyAidlInterface这个接口,从名字上面可以看到这是一个代理类。这里其实就是一个服务端跟客户端。现在,假设有两个进程,A和B ,当A向B发消息的时候,会通过代理类Proxy,向Binder 发送消息,然后Binder 再把这个消息发给进程B的Stub对象,完成一次进程间通信,这里也充分的说明了Binder 既可以是服务端也可以是客户端,主要取决于是接受消息还是发送消息。

在Proxy这个代理类里面有这样一段代码


Parcel _data = Parcel.obtain();                Parcel _reply = Parcel.obtain();                try {                    _data.writeInterfaceToken("com.rxdemo.IMyAidlInterface");                    _data.writeInt(anInt);                    _data.writeLong(aLong);                    _data.writeInt(aBoolean?1:0);                    _data.writeFloat(aFloat);                    _data.writeDouble(aDouble);                    _data.writeString(aString);                    this.mRemote.transact(1, _data, _reply, 0);                    _reply.readException();                } finally {                    _reply.recycle();                    _data.recycle();                }

注意到没有,创建了一个reply对象,然后 通过 this.mRemote.transact 方法将数据打包发送出去。这个方法是非常重要的,客户端向服务端发送消息就是通过这个方法,也就是说这个方法是在客户端的。我们再看 Stub这个类里面是怎么接收消息的。
 public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {            switch(code) {            case 1:                data.enforceInterface("com.rxdemo.IMyAidlInterface");                int _arg0 = data.readInt();                long _arg1 = data.readLong();                boolean _arg2 = 0 != data.readInt();                float _arg3 = data.readFloat();                double _arg4 = data.readDouble();                String _arg5 = data.readString();                this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);                reply.writeNoException();                return true;            case 1598968902:                reply.writeString("com.rxdemo.IMyAidlInterface");                return true;            default:                return super.onTransact(code, data, reply, flags);            }        }
onTransact 这个方法是在服务端的,用于接收消息。

在Stub这个类里面有一个asInterface方法,

public static IMyAidlInterface asIn                return null;terface(IBinder obj) {
            if(obj == null) {
} else { IInterface iin = obj.queryLocalInterface("com.rxdemo.IMyAidlInterface"); return (IMyAidlInterface)(iin != null && iin instanceof IMyAidlInterface?(IMyAidlInterface)iin:new IMyAidlInterface.Stub.Proxy(obj)); } }

queryLocalInterface这个方法里面传入了一个参数,这个参数是descriptor用来表示唯一的AIDL文件,我们看asInterface这个方法返回的逻辑是什么,表示,如果能找到这个iin对象,表示在同一个进程,如果找不到,就需要通过代理对象去实现远程通信,不在一个进程。

我们在启动一个Service的时候,会去使用binderService这个方法来绑定一个服务,可以是同进程的,也可是远程的。

 this.bindService(intent, new ServiceConnection() {            @Override            public void onServiceConnected(ComponentName name, IBinder service) {                try {                    IMyAidlInterface asInterface=IMyAidlInterface.Stub.asInterface(service);                    asInterface.testBinder();                } catch (RemoteException e) {                    e.printStackTrace();                }            }            @Override            public void onServiceDisconnected(ComponentName name) {            }        },);

在service bind成功之后,就可以通过IMyAIDLInterface 这个接口对象去调用远程的方法了。







原创粉丝点击