3.3 AIDL跨应用服务
Android Telephony框架层提供了一些AIDL系统服务以及第三方应用,通过这些系统服务可方便地获取手机当前状态,如待机状态、通话中、来电等,这些状态在Android手机应用开发中非常重要。如果我们开发一款Android平台的手机音乐播放器,在使用音乐播放器的过程中需要知道手机当前是什么状态,如果是通话中或来电,这时就必须暂停播放音乐。
3.3.1 AIDL概念分析
什么是AIDL呢?AIDL的Android官方定义如下:
AIDL (Android Interface Definition Language) is similar to other IDLs you might have worked with. It allows you to define the programming interface that both the client and service agree upon in order to communicate with each other using interprocess communication (IPC). On Android, one process cannot normally access the memory of another process. So to talk, they need to decompose their objects into primitives that the operating system can understand, and marshall the objects across that boundary for you. The code to do that marshalling is tedious to write, so Android handles it for you with AIDL.
AIDL(Android Interface Definition Language,Android 接口定义语言) Android系统平台的接口定义语言与您可能已经使用过的其他IDLs接口定义语言相似。程序员可以利用AIDL自定义编程接口,在客户端和服务端之间实现进程间通信(IPC)。在Android平台上,一个进程通常不能访问另外一个进程的内存空间,因此,Android平台将这些跨进程访问的对象分解成操作系统能够识别的简单对象, 并且为跨应用访问而特殊编排和整理这些对象。用于编排和整理这些对象的代码编写起来非常冗长,所以Android的AIDL提供了相关工具来自动生成这些代码供程序员使用。
根据前面对AIDL的概念分析,本例创建两个Android APK应用,一个应用作为AIDL服务提供方,而另外一个应用作为AIDL服务调用方,这样才能体现出AIDL跨应用服务访问。首先开始编写AIDL服务提供方。
3.3.2 AIDL服务提供方
先进入AIDL服务提供方的工程,创建.adil文件。本例中,在com.myandroid.aidl包下定义了一个IMyService.aidl文件,它仅有一个接口helloAndroidAIDL,形参为String name,返回String对象。代码如下:
package com.myandroid.aidl;
interface IMyService {
String helloAndroidAIDL(String name);
}
Eclipse ADT开发插件安装正确以及AIDL文件定义正确的情况下,ADT开发插件会在工程中gen目录下创建对应的IMyService.java文件,其包路径与AIDL文件定义的路径一致,为com.myandroid.aidl。
注意 不难发现,AIDL接口定义语言与Java语言非常相似;另外,可以定义一些复合的数据类型,本例中使用Java基本的数据类型,感兴趣的读者可以上网查阅Android Parcel。
接着实现helloAndroidAIDL接口的逻辑实现,在AIDL接口定义文件一致的包路径com.myandroid.aidl下创建MyService类,继承android.app.Service类,并有一个内部类实现了IMyService.Stub接口,具体代码如下:
public class MyService extends Service {
// 内部类MyServiceImpl,继承了AIDL工具生成的IMyService.Stub接口,主要实现之前定义的//接口helloAndroidAIDL
public class MyServiceImpl extends IMyService.Stub {
@Override//helloAndroidAIDL接口具体实现,在服务端输出日志,返回字符串
public String helloAndroidAIDL(String name) throws RemoteException {
System.out.println("helloAndroidAIDL:" + name);
return "AIDL MyService return value";
}
}
@Override
public IBinder onBind(Intent intent) {//AIDL固定写法
return new MyServiceImpl();//返回IMyService.Stub子类对象
}
}
实现MyService类后,还需要对此AIDL服务进行配置,以便Android平台能识别此服务。打开AndroidManifest.xml文件开始配置,在application节点里加入如下xml配置信息:
<service android:name="com.myandroid.aidl.MyService">//名称需要和我们实现的Java代码//类名称保持一致
<intent-filter>
<action android:name="com.myandroid.aidl.IMyService" />//一般采用实现的Java
//代码类名称,方便开发者理解和记忆并且名称不容易重复
</intent-filter>
</service>
最后发布并运行此Android Project,在Eclipse中单击此Android Project,然后单击右键弹出菜单选项,进入Run As,选择Android Application,接着会启动Android虚拟设备,并会将此工程发布在启动的Android虚拟设备上运行。
Android提供的AIDL工具非常方便,会在很短的时间内完成AIDL服务端框架。接下来进入AIDL调用方代码实现。
3.3.3 AIDL服务调用方
首先建立AIDL服务调用方的Android Project,AIDL的调用主要有三大步骤:
步骤1 将AIDL服务端生成的Java文件com/myandroid/aidl/IMyService.java复制到本工程,尽量不要改变其包路径和文件名,这样客户端也就知道服务端具有什么样的能力,即AIDL服务端的接口定义。
步骤2 编写代码绑定服务,获取ADIL服务对象。
步骤3 通过AIDL服务对象完成AIDL接口调用。
首先在需要调用Activity中定义IMyService myService服务调用对象,然后创建serviceConnection对象,服务绑定相关代码如下:
private ServiceConnection serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
// 获得AIDL服务对象
myService = IMyService.Stub.asInterface(service);
button2.setEnabled(true);
}
public void onServiceDisconnected(ComponentName name) {
}
}
//绑定服务,此代码可放入特定事件或一些触发事件
Intent intent = new Intent("com.myandroid.aidl.IMyService");
bindService(intent, serviceConnection,
Context.BIND_AUTO_CREATE);
AIDL服务调用代码如下:
String msg = myService. helloAndroidAIDL("Other Application");
Toast toast = Toast.makeText(AidlClient.this, msg, Toast.LENGTH_SHORT);
toast.show();
调用服务端的helloAndroidAIDL方法,服务端会打印出“helloAndroidAIDL: Other Application”日志,调用端通过Toast显示服务端返回String,内容为“AIDL MyService return value”。这样,AIDL服务端和客户端完成了完整交互流程。
注意 本例的AIDL服务调用与AIDL提供的系统服务调用方式不太一样,Android系统服务调用不需要程序员绑定服务,而直接获取系统服务对象即可调用其服务接口;在Android SDK级别上的开发无法实现系统服务,只有在修改Android源代码时可实现自己的系统服务,详细内容在后面的章节讲述。