在AIDL编程环境里实际上是支持反向调用的,原理跟我们实现一个Remote Service一样,就是通过把Proxy与Stub反过来,就得到了这样的回调式的aidl编程。唯一的区别是,当我们的Stub在Activity时实现时,我们实际上跟后台线程执行也没有区别,Callback并非是在主线程里执行的,于是不能进行重绘界面的工作。于是,我们必须像后台线程编程一样,使用Handler来处理界面显示处理。
前面我们说过aidl是可以互相引用的,于是我们可以借用这样的机制,通过引用另一个新增的aidl文件来加强我们前面的单向的TaskService版本。我们先增加一个新的ITaskServiceCallback.aidl文件,与ITaskService保持同一目录:
在这一定义里,我们新增加了一个ITaskServiceCallback的接口类,基本上与我们前面的ITaskService.aidl一样,在这个接口类里,我们新加了一个valueCounted()方法,这一方法将会被Service所使用。
在这个文件里,唯一与ITaskService.aidl不同之处在于,我们使用了一个oneway的标识符,oneway可以使aidl调用具有异步调用的效果。在默认情况下,基于aidl的调用都会等待远端调用完成之后再继续往下执行,但有时我们可能希望在跨进程调用会有异步执行的能力,我们在发出调用请求后会立即返回继续执行,调用请求的结果会通过其他的callback返回,或是我们干脆并不在乎成功与否,此时就可以使用oneway。当然,从我们前面分析aidl底层进行的工作,我们可以知道,所谓的远程调用,只不过是通过Binder发送出去一个命令而已,所以在aidl里面如果使用了oneway限定符,也就是发送了命令就收工。
然后,我们修改一下我们的ITaskService.aidl,使我们可以使用上这个新加入的回调接口:
我们会引用前面定义好的ITaskServiceCallback.aidl文件,通过包名+接口的方式进行引用。为了省事,我们直接在原来的getPid()方法里进行修改,将新定义的ITaskServiceCallback接口类作为参数传递给getPid()接口。于是,在Service端Stub对象里实现的getPid()方法,将可以使用这一回调对象:
加入了回调之后的代码结构并没有大变,只增加了3部分的内容,通过这三部分的内容,我们此时便可以记录我们的getPid()总共被调用了多少次。
加入了回调之后,对Service端的实现并没有增加多大的工作量,因为作为回调,实现是放在客户端上来完成的。
因为我们的aidl接口已经发生了变动,于是需要将新加的ITaskServiceCall.aidl与改变过的ITaskService.aidl文件拷贝到应用程序工程里。我们再来看一下客户端实现代码需要作怎样的调整:
新加入的回调接口对象,也没给我们带来多大的麻烦,我们最重要是提供一个实例化的ITaskServiceCallback.Stub对象,然后通过getPid()将这一远程对象的引用发送给Service端。之后,Service处理后的回调请求,则会通过Binder会回到客户端,调用在Stub对象里实现的回调方法。所以我们实际增加的工作量,也仅是写一个ITaskServiceCallback.Stub接口类的实现而已:
从上面的代码可以看出,我们通过aidl创建回调方法好像比我们直接通过aidl写一个Remote Service还要简单。事实上,并非回调创建方便,在原则上,我们本只需要一个Stub对象便可以得到我们想要的RPC能力了,只不过出于管理存活周期的需要,才融入到了Service管理框架里,因为这种Service使用上的需求才带来了一些编程上的开销