关于第二个问题,Android的SDK中提供了一个aidl工具,该工具可以把一个aidl文件转换为一个Java类文件,在该Java类文件,同时重载了transact和onTransact()方法,统一了存入包裹和读取包裹参数,从而使设计者可以把注意力放到服务代码本身上。
aidl工具不是必需的,对于有经验的程序员来讲,手工编写一个参数统一的包裹存入和包裹读出代码并不是一件复杂的事情。
接下来看aidl工具都做了什么。如本章第一节示例,此处依然假设要编写一个MusicPlayerService服务,服务中包含两个服务函数,分别是start()和stop()。那么,可以首先编写一个IMusicPlayerService.aidl文件。如以下代码所示:
package com.haiii.android.client;
interface IMusicPlayerService{
boolean start(String filePath);
void stop();
}
该文件的名称必须遵循一定的规范,第一个字母"I"不是必需的,但是,为了程序风格的统一,"I"的含义是IInterface类,即这是一个可以提供访问远程服务的类。后面的命名--MusicPlayerService对应的是服务的类名,可以是任意的,但是,aidl工具会以该名称命名输出的Java类。这些规则都只是Eclipse下ADT插件的默认规则,aidl本身只是一个命令行程序,借助命令行的话,则可以灵活指定输出文件的名称及位置。具体使用方法参照aidl的执行帮助信息。
http://developer.android.com/guide/components/aidl.html
aidl文件的语法基本类似于Java,package指定输出后的Java文件对应的包名。如果该文件需要引用其他Java类,则可以使用import关键字,但需要注意的是,包裹内只能写入以下三个类型的内容。
- Java原子类型,如int、long、String等变量。
- Binder引用。
- 实现了Parcelable的对象。
因此,基本上来讲,import所引用的Java类也只能是以上三个类型。
interface为关键字,有时会在interface前面加一个oneway,代表该service提供的方法都是没有返回值的,即都是void类型。
下面看看该aidl生成的IMusicPlayerService.java文件的代码。如下所示:
package com.haiii.client;
public interface IMusicPlayerService extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder
implements com.haiii.client.IMusicPlayerService {
private static final java.lang.String DESCRIPTOR =
"com.haiii.client.IMusicPlayerService";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.haiii.client.IMusicPlayerService interface,
* generating a proxy if needed.
*/
public static com.haiii.client.IMusicPlayerService
asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin =
(android.os.IInterface) obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.haiii.client.IMusicPlayerService))) {
return ((com.haiii.client.IMusicPlayerService) iin);
}
return new com.haiii.client.IMusicPlayerService.Stub.Proxy(obj);
}
public android.os.IBinder asBinder() {
return this;
}
@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_start: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
boolean _result = this.start(_arg0);
reply.writeNoException();
reply.writeInt(((_result) ? (1) : (0)));
return true;
}
case TRANSACTION_stop: {
data.enforceInterface(DESCRIPTOR);
this.stop();
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.haiii.client.IMusicPlayerService {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
public boolean start(java.lang.String filePath) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
boolean _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(filePath);
mRemote.transact(Stub.TRANSACTION_start, _data, _reply, 0);
_reply.readException();
_result = (0 != _reply.readInt());
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public void stop() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_stop, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_start = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_stop = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public boolean start(java.lang.String filePath) throws android.os.RemoteException;
public void stop() throws android.os.RemoteException;
}