AIDL远程回调实现

来源:互联网 发布:网站seo综合诊断 编辑:程序博客网 时间:2024/06/13 17:59

官方文档:http://developer.android.com/guide/components/aidl.html

一、AIDL基本概念
AIDL是Android Interface Definition Language的缩写,目的是有一套规范来实现服务和客户端的互相调用。但是一般来说我们的服务和客户端是存在于不同的进程的(注:是进程Process不是线程Thread)。如果有用过CORBA,COM或者RMI,那对于IDL就不陌生了。

先来复习下,之前的一篇文章有讲过Linux的内存管理模式:

逻辑地址:段地址和位移来进行定位
虚拟地址:每个进程都会有一个用户地址和虚拟地址空间(就是它)
物理地址:代表了真实的RAM,就是大家经常说的几个G内存代表了它的最大寻址空间。

众所周知,进程与进程之间的内存一般来说都不是共享的。这是因为每个进程在建立的时候会向操作系统申请一块内存来保存自己的数据,一旦创建成功这段内存(地址段)就会划归这块内存为该进程独享(虚拟地址段)。每个进程分配的内存都是同一个物理内存里面的一部分,如果进程间需要相互调用或者传递消息,一种方案就是把这部分内存在多个进程中共享,需要就去访问,就叫做IPC(Interprocess Communication)是进程间通信最快的方式。

在Google的官方文档中提到一段关于AIDL的特别说明,这一点我认为非常重要,在开发中需要特别注意。
总结如下:
1. AIDL的调用是方法的调用,这些方法的调用可能存在于不同的线程中(Main thread,Local thread,Remote thread),也就是说可能来自客户端UI线程,本地线程,甚至自己进程中的线程,因此你必须时刻准备应付来自不同线程的调用。
2. 方法的参数有输入(in)输出(out)标签,熟悉C#的人一下就会明白这个直接是语言特性(多返回值),这就代表这个方法的参数是输入还是输出(约等于返回)。
in标记:这个方法的参数如果远程对象状态被改变但是调用方对象状态是不会改变的。
out标记:这个方法的参数如果远程对象状态被改变会影响调用方的对象状态改变。
3. oneway关键字:如果申明这是一个oneway的状态,那么在处理方法的时候不会阻塞,直接将值复制完成之后立即返回。

二、AIDL实现
1. 定义AIDL描述文件:
AIDL的文件以.aidl结尾,通过IDE可以很方便的进行编写,但是IDE不会实时编译这些文件(未实现)。如果要编译这些文件需要手动调用Compile,然后SDK Tools会把你的描述文件生成必要的代码(继承IBinder,实现必要的方法)然后放入gen/目录下。需要注意的是,这些AIDL描述文件必须要客户端和服务端保持一致,也就是说一旦服务端有所修改必须要将这些文件下发给客户端编译使用,不灵活,但是也很无奈。所以在设计AIDL接口的时候一定要注意到可扩展性,倘若有新的功能需要实现,就只好再引入新的AIDL描述文件和接口了。
在AIDL描述文件中可以使用原始类型int, long, float, double, String,如果有复杂对象需要传输则必须实现Parcelable接口,以及List,Map。
2. 服务端接口实例
先来看一段我们现有项目的AIDL描述:

// IWebSocketService.aidlpackage com.xingren.service.aidl;// Declare any non-default types here with import statementsimport com.xingren.service.aidl.IWebSocketCallback;import com.xingren.service.ws.Packet;interface IWebSocketService {    // Get connection state    String getConnectionState();    // Is web socket connected    boolean isConnected();    // Disconnect web socket    void disconnect();    // Register callback    void registerCallback(String stanza, IWebSocketCallback callback);    // Unregister callback    void unregisterCallback(String stanza, IWebSocketCallback callback);    // Make a request to server.    Packet request(String guid, String data);    // Send message to server, oneway unblock    void send(String data);}

这里面有基本类型String还有一个接口IWebSocketCallback,它其实也是一个AIDL接口:

// IWebSocketCallback.aidlpackage com.xingren.service.aidl;// Declare any non-default types here with import statementsimport com.xingren.service.ws.Packet;interface IWebSocketCallback {    // Call back    void callback(in Packet packet);}

我们后面来讲为什么有这样一个接口,先来看看IWebSocketService里面的内容。
除了有两个回调接口的方法以外,还有一些其他简单方法,这些都是将要暴露给客户端的方法,它的具体实现如下:

    private final IWebSocketService.Stub mBinder = new IWebSocketService.Stub() {        @Override        public String getConnectionState() throws RemoteException {            return ProviderService.getInstance().getWSController().getConnectionState().name();        }        @Override        public boolean isConnected() throws RemoteException {            return ProviderService.getInstance().getWSController().isConnected();        }        @Override        public void disconnect() throws RemoteException {            ProviderService.getInstance().getWSController().disconnect();        }        @Override        public void registerCallback(String stanza, IWebSocketCallback callback) throws RemoteException {            Logger.d(TAG, "Callback registered.");            Stanza key = Stanza.valueOf(stanza);            LinkedList<IWebSocketCallback> linkedList = mCallbackMap.get(key);            if (linkedList == null) {                linkedList = new LinkedList<>();                mCallbackMap.put(key, linkedList);            }            linkedList.add(callback);        }        @Override        public void unregisterCallback(String stanza, IWebSocketCallback callback) throws RemoteException {            Logger.d(TAG, "Callback unregistered.");            LinkedList<IWebSocketCallback> linkedList = mCallbackMap.get(Stanza.valueOf(stanza));            if (linkedList != null) {                linkedList.remove(callback);            }        }        @Override        public void send(String data) {            Logger.d(TAG, "Send data: " + data);            ProviderService.getInstance().getWSController().send(data);        }        @Override        public Packet request(String guid, String data) {            RequestHandler requestHandler = RequestHandler.obtain(guid, null);            mRequestResponseCache.put(guid, requestHandler);            return requestHandler.request();        }    };

可以看到我们new出来的是一个IWebSocketService的内部类Stub,这个类就是由Android SDK帮我们生成的(所以一定要编译之后才能够拿到这个类)。
接下来就需要再实现一个Service,在onBind的时候将这个实例返回回去,我这里是将这个实例放到了Dispatcher这个单例的类中了。

    @Override    public IBinder onBind(Intent intent) {        return Dispatcher.INSTANCE.getBinder();    }
  1. 客户端实例
    客户端如何去写绑定Service的这里就略过不提了,提供一下ServiceConnection实例
    private IWebSocketService mService;    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            if (mBound) {                return;            }            mService = IWebSocketService.Stub.asInterface(service);            onRegisterCallback();            mBound = true;        }        @Override        public void onServiceDisconnected(ComponentName name) {            try {                onUnregisterCallback();            } catch (RemoteException e) {                e.printStackTrace();            }            mBound = false;        }    };

AIDL的服务就写好了,客户端也绑定了,接下来我们看看复杂类型的传递和回调的实现。

三、回调的实现
IWebSocketService中有一个方法

    // Make a request to server.    Packet request(String guid, String data);

这个方法是阻塞式的发一个请求,返回值是一个Packet。Packet是一个Parcelable的实例,Parcelable实现就不讲了,我们看看它的aidl描述文件(每个需要在AIDL接口中使用的复杂类型都必须要有对应的描述文件):

// Packet.aidlpackage com.xingren.service.ws;parcelable Packet;

这样就可以实现复杂类型的传递。
那回调如何实现呢?
AIDL中可以作为参数的还可以是另外一个AIDL接口,IWebSocketService的描述中有两个方法:

    // Register callback    void registerCallback(String stanza, IWebSocketCallback callback);    // Unregister callback    void unregisterCallback(String stanza, IWebSocketCallback callback);

这两个方法里面的IWebSocketCallback就是AIDL接口,那我们再看看IWebSocketService实现中这两个方法分别干了什么事情:

        @Override        public void registerCallback(String stanza, IWebSocketCallback callback) throws RemoteException {            Logger.d(TAG, "Callback registered.");            Stanza key = Stanza.valueOf(stanza);            LinkedList<IWebSocketCallback> linkedList = mCallbackMap.get(key);            if (linkedList == null) {                linkedList = new LinkedList<>();                mCallbackMap.put(key, linkedList);            }            linkedList.add(callback);        }        @Override        public void unregisterCallback(String stanza, IWebSocketCallback callback) throws RemoteException {            Logger.d(TAG, "Callback unregistered.");            LinkedList<IWebSocketCallback> linkedList = mCallbackMap.get(Stanza.valueOf(stanza));            if (linkedList != null) {                linkedList.remove(callback);            }        }

我们将这两个回调保存到了一个Concurrent的HashMap里面,等到其他事件触发时,我们直接执行callback方法,将Packet传回去:

for (IWebSocketCallback callback : callbackLinkedList) {                try {                    callback.callback(packet);                } catch (RemoteException e) {                    Logger.e(TAG, e);                }            }

Google的官方文档写得很好,建议大家再按照官方文档实操一次就可以完全掌握了。

0 0
原创粉丝点击