Android AIDL整理

来源:互联网 发布:淘宝店铺全屏海报 编辑:程序博客网 时间:2024/06/05 17:58

都说AIDL是为了处理跨进程通讯而出现的,那么除了AIDL,到底还有哪些方法可以实现Android跨进程通讯呢?
1.Broadcast 广播
对于接收系统广播我们已经习以为常了,如,锁屏,电量不足,截屏等等。
Apps can also initiate broadcasts—for example, to let other apps know that some data has been downloaded to the device and is available for them to use.
这段话的大概意思就是,app本身可以初始化一个广播(自定义Action),比如,通过指定Action发送广播让其他app知道数据被下载好了,可以使用了等等消息.
2.Messenger
后面专门写一篇
3.AIDL
直接进入正题吧
这是一篇以新手的角度分析AIDL的文章,入门级。
困惑一
客户端,服务端?
并不是C/S架构中的客户端和服务端,在Android中,他们都是运行在你手机里的app,处于不同进程中。服务端是提供服务的app,比如,他提供一个计算位置的服务,在你的app中,调用服务端的服务后,传入经纬度,他返回给你一个位置信息。
困惑二
太多Binder,客户端Binder,服务端Binder,底层Binder了。
写到这里,想起了往事,借老炮儿的一句台词,”我就CTM了”,非此语不能言我心智!
在网上找了几张图
这里写图片描述
这张图是以操作系统的视角来描述这个抽象的过程,鄙人看过一点<现代操作系统>,仅仅了解一点概念,但仍然觉得获益匪浅啊。一个你无法逾越的障碍就是不同进程间地址空间不一样,即数据是无法访问到的.又copy一段话说,“Binder机制的初衷就是,让请求和响应被写在一个所有进程都可以访问的地方,这个地方就是Kernel Space!”Binder Driver就是负责这里的读写操作(鄙人狭义的理解,望指正).再摘抄一点IBinder API的解释。“IBinder中有个transact()方法,这个方法和Binder中的onTransact()方法匹配。前者可以让你向IBinder对象中写入数据,后者可以让你在Binder对象中接受数据”.鄙人认为,这里的IBinder对象就是我们的客户端,Binder对象就是我们的服务端.
再看一幅图,鄙人的水平,暂时只能看别人的图说话了!
这里写图片描述
下面上代码吧,网上借鉴的经典模型,一个Activity,一个Service,其中service运行在remote进程中。Activity是客户端,里面有一个Button,点击之后,传入两个int参数,调用service的calculate(int a,int b)方法,返回一个int,完毕!
AIDL接口为 Cal_interface ,服务为mAIDLService.
1.先从客户端拿到的东西说起 service
我们都知道,在同进程中绑定Service时,返回的是确确实实的Service中的Binder的引用,但是,这里是跨进程,我们是不可能拿到其他进程的app的引用的。那么,这个service究竟是个什么东西呢,经过鄙人打印得证,是个BinderProxy,他是Binder的一个内部类,但究竟是在哪蹦出来的,我还不清楚。
鄙人的理解是,既然客官要进行IPC操作,那我就给你个客户端Binder吧,你在里面写写画画,我负责把参数带到服务端去。
在onServiceConnected方法中,拿到这么个service
Cal_interface mService = Cal_interface.Stub.asInterface(service);

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

下面来看一看asInterface()方法做了哪些事情,我们将拿到的BinderProxy传进去,注意,该方法是运行在客户端进程中的。queryLocalInterface()方法由Binder实现,根据描述符确定这个Binder在该进程中存不存在,如果存在,直接返回,不存在返回null。因为这里是跨进程,当我们发现返回值为null时,我们在客户端创建出一个Proxy返回,这个Proxy实现了Cal_interface接口,所以我们可以调用他的calculate()方法。

 private final Cal_interface.Stub mBinder = new Cal_interface.Stub() {        @Override        public int calculate(int anInt, int aLong) throws RemoteException {            Log.i(TAG, "计算中......");            return anInt + aLong;        }    };

上面这一段是运行在服务端的代码,那么,就鄙人理解,使用Binder时,跨不跨进程的根本区别就是
“你拿到的Binder对象的区别!”在同进程中,你拿到的Binder确确实实是Service中的Binder,在跨进程中,你拿到的是系统为你创建的BinderProxy(Binder内部类)。为毛,因为进程之间不能直接访问嘛,必须要系统(Kernel)来帮你做这件事!

return new com.example.pj.aidl_demo.Cal_interface.Stub.Proxy(obj);

返回的是一个Proxy,该代理类实现了我们的Cal_interface接口,内部持有BinderProxy的引用(即构造函数中的obj)。
下面来看一看当我们调用服务的calculate()方法时,发生了什么!
我们之前说过,返回的是一个Proxy的实现类,实现了Cal_interface接口那么进入他的calculate()方法看一看。

 @Override            public int calculate(int anInt, int aLong) 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(anInt);                    _data.writeInt(aLong);                    mRemote.transact(Stub.TRANSACTION_calculate, _data, _reply, 0);                    _reply.readException();                    _result = _reply.readInt();                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }

这个过程就是,拿到了两个包裹,你要传进去的参数_data,你希望拿到的返回值_reply,这里的mRemote(本地的Binder,千万别天真的以为是服务端进程的,哈哈)就是我们就是系统给你创建出的BinderProxy,他是Binder的内部类,所以直接调用transact()方法。
mRemote.transact(Stub.TRANSACTION_calculate, _data, _reply, 0);
他的意思就是拿着我传进来的_data,把计算出的数据放入_reply中,开始进行真正的计算吧!注意:这个线程会一直阻塞在这里,直到服务端给出返回值!
再进一步说,鄙人认为,当客户端调用transact()方法后,会由底层的Binder来打通剩下的关节,通知服务端的Binder调用onTransact()方法进行真正的计算。

 @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_calculate: {                    data.enforceInterface(DESCRIPTOR);                    int _arg0;                    _arg0 = data.readInt();                    int _arg1;                    _arg1 = data.readInt();                    int _result = this.calculate(_arg0, _arg1);                    reply.writeNoException();                    reply.writeInt(_result);                    return true;                }            }            return super.onTransact(code, data, reply, flags);        }

这里的this就是Stub对象,即服务端的那个Binder对象
int _result = this.calculate(_arg0, _arg1);
根据图上所示,服务端Binder计算出result后,同样通过底层Binder驱动打通剩下的环节,把数据传到客户端Binder那里!

0 0
原创粉丝点击