本章详解AIDL(服务)
android系统中的Service主要有两个作用:后台运行和跨进程通讯。后台运行就不用说了,当Service启动后,就可以在Service对象中 运行相应的业务代码,而这一切用户并不会察觉。而跨进程通讯是这一节的主题。如果想让应用程序可以跨进程通讯,就要使用我们这节讲的AIDL服 务,AIDL的全称是Android Interface Definition Language,也就是说,AIDL实际上是一种接口定义语言。
1.在eclipse中创建aidl文件
先选中要创建aidl文件的包
2.然后File->New->File,弹出对话框:然后在 File name 输入框里输入文件名,以.aidl后缀结尾,如:XXX.aidl最后,finish即可。然后在aidl文件中给出正确的aidl接口代码,保存。系统会自动生成其实现类源文件:XXX.Java
最后保存成功后会在gen/相同的包名下生成.java文件。
3.官方文档介绍AIDL
“只有当你允许来自不同的客户端访问你的服务并且需要处理多线程问题时你才必须使用AIDL”,其他情况下你都可以选择其他方法,如使用Messager,也能跨进程通讯。可见AIDL是处理多线程、多客户端并发访问的。而Messager是单线程处理。
4.Proxy,Stub,asInterface
在上述gen/相同的包名下生成的.java文件中可读性上不是很好,但不影响我们进行分析。这段自动生成的代码中,最重要的是三个部分,Proxy,Stub和asInterface。
4.1.Proxy类 实现定义AiBackService.aidl的接口
实现接口定义的方法
例如在上述AiBackService.aidl中定义
package com.mealo.service;
interface AiBackService{
void Post(String json);
void StartReceiveFile();
boolean IsAlive();
void GetImgFile();
void GetFood();
void UpdFood();
void SetFoodClasses();
void StopConn();
void StartConn();
boolean WaitAlive(int totalTime);
}
在相应的AiBackService.java中的
private static class Proxy implements com.mealo.service.BiBackService
{
方法里操作
void Post(String json);
void StartReceiveFile();
boolean IsAlive();
void GetImgFile();
void GetFood();
void UpdFood();
void SetFoodClasses();
void StopConn();
void StartConn();
boolean WaitAlive(int totalTime);
方法的实现
}
4.2.Stub 类继承Binder并实现AiBackService.aidl的接口
public static abstract class Stub extends android.os.Binder implements com.mealo.service.BiBackService
{
}
4.3.asInterface(方法)
Proxy 这里是private权限的,外部是无法访问的,但这里是 Android 有意为之,抛出了 asInterface 方法,这样避免了对 Proxy可能的修改。
总结
Proxy 是写入参数,读取值;Stub 是读取参数,写入值。正好是一对,那因此我们是不是可以做出这样的论断呢?Proxy 和 Stub 操作的是一份数据?恭喜你,答对了。
根据故事讲述AIDL
自动售货机的故事
先偏一个题,夏天一到,天气也变得炎热,地铁旁边的自动售货机开始有更多的人关顾了。那么售货机是怎么工作的了?通过对这个分析,可以对Binder Proxy/Stub 模式更好地理解。
和我们打交道得是售货机,而不是背后的零售商,道理很简单,零售商的位置是固定的,也就意味着有很大的交通成本,而和售货机打交道就轻松很多,毕竟售货机就在身边。因而从客户端的角度上看,只需要知道售货机即可。
再从零售商的角度来看,要和为数众多的售货机打交道也是不容易的事情,需要大量的维护和更新成本,如果将起交由另一个中介公司,就能够省心不少。零售商只关心这个中介公司,按时发货,检查营收,这就是所有它应该完成的工作。
如上图所示,在 Binder Framework 中也采用了类似的结构,Proxy 就相当于前面提及的售货机,Stub 相当于这里的中介公司,在这样的设计下,客户端只需要和 Proxy 打交道,服务端只需要和 Stub 打交道,调理清晰很多。这种模式又被称为 Proxy / Stub 模式,这种模式也值得我们在日后的开发中借鉴。另外需要注意的是,为了开发的需要,通常 Proxy 和 Stub 实现了相同的接口。
在这里 Stub 是具体的远程方法实现,也被称为存根,Proxy 继承了相应的接口,但只是在这里面调用远程方法,并返回相应的结果。
Android 的设计者在一开始也意识到这个问题,推出了 AIDL,如下图所示
Caller:调用者(客户端) Callee:被调用者(服务器)
5.系统使用AIDL的场景
AIDL 被Android 系统广泛使用,在许多地方都能看到相应的场景,这里以 电话服务 作为例子,来简单说明下如何在系统没有提供挂断电话的API的情况下强行挂电话。
通过的 ITelephony.aidl 的查看,可以在里面找到相应的接口,不过这个类是 internal 包里面的,应用层无法访问。那怎么来完成这个操作了?
/**
* End call if there is a call in progress, otherwise does nothing.
*
* @return whether it hung up
*/
boolean endCall();
首先在相同包名下申明同样的AIDL文件,再通过编译后,就会生成相应的 Stub 文件。其次通过反射拿到 TELEPHONY_SERVICE 的binder。这个 binder 就是可以操作另一个进程来挂断电话的句柄。将这个binder 作为 Proxy 的参数,并通过 asInterface 注入进去,从何获得相应的接口,最后调用 telephony.endCall() 即可完成操作。
Method method = Class.forName("android.os.ServiceManager").getMethod("getService", String.class);
IBinder binder = (IBinder) method.invoke(null, new Object[]{TELEPHONY_SERVICE});
ITelephony telephony = ITelephony.Stub.asInterface(binder);
telephony.endCall();