Since each application runs in its own process, and you can write a service that runs in a different process from your Application's UI, sometimes you need to pass objects between processes. On the Android platform, one process can not 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 object across that boundary for you.
通常每个应用程序都在它自己的进程内运行,但有时需要在进程间传递对象,你可以通过应用程序UI的方式写个运行在一个不同的进程中的 service。在AndRoid平台中,一个进程通常不能访问其他进程中的内存区域。所以,他们需要把对象拆分成操作系统能理解的简单形式,以便伪装成 对象跨越边界访问。
The code to do that marshalling is tedious to write, so we provide the AIDL tool to do it for you.
编写这种伪装代码相当的枯燥乏味,好在我们提供了AIDL工具可以来做这件事。
AIDL (Android Interface Definition Language) is an IDL language used to generate code that enables two processes on an Android device to talk using interprocess communication (IPC). If you have code in one process (for example, in an Activity) that needs to call methods on an object in another process (for example, a Service), you would use AIDL to generate code to marshall the parameters.
AIDL(AndRoid接口描述语言)是一个IDL语言,它可以生成一段代码,可以使在一个AndRoid设备上运行的两个进程使用内部通信进程 进行交互。如果你需要在一个进程中(例如:在一个Activity中)访问另一个进程中(例如:一个Service)某个对象的方法,你就可以使用 AIDL来生成这样的代码来伪装传递各种参数。
The AIDL IPC mechanism is interface-based, similar to COM or Corba, but lighter weight. It uses a proxy class to pass values between the client and the implementation.
AIDL IPC的机制是基于接口的,和COM或Corba类似,但它是轻量级的。它使用代理类在客户端和实现层间传递值。
This page includes the following main topics:
本页包含以下主题:
Implementing IPC Using AIDL
Calling an .aidl (IPC) Class
使用AIDL实现IPC
调用一个AIDL(IPC)类
[ 编辑 ] 使用AIDL实现IPC
Follow these steps to implement an IPC service using AIDL.
使用AIDL实现一个IPC有下列步骤:
1.Create your .aidl file - This file defines an interface (YourInterface.aidl) that defines the methods and fields available to a client.
1、创建你的AIDL文件 - 这个文件定义一个接口(YourInterface.aidl),该接口定义了可供客户端访问的方法和属性。
2.Add the .aidl file to your makefile - (the Eclipse plugin manages this for you). Android includes the compiler, called AIDL, in the tools/ directory.
2、添加AIDL文件到你的makefile中-(Eclipse plugin可以帮你管理)。AndRoid包括编译器,AIDL调用,这些都能在tools/directory中找到。
3.Implement your interface methods - The AIDL compiler creates an interface in the Java programming language from your AIDL interface. This interface has an inner abstract class named Stub that inherits the interface (and implements a few additional methods necessary for the IPC call). You must create a class that extends YourInterface.Stub and implements the methods you declared in your .aidl file.
3、实现接口方法-AIDL编译器从你的AIDL接口中使用JAVA编程语言来创建一个接口。这个接口有一个名为Stub的内部抽象类,它继承接口 (并实现供IPC调用的所必需的几个附加方法)。你必须创建一个类来实现该接口。
4.Expose your interface to clients - If you're writing a service, you should extend Service and override getBinder() to returning an instance of your class that implements your interface.
4、向客户端开放接口-如果你写个service,你应该扩展该Service并重载getBinder()方法来返回一个实现上述接口的类的实 例。
创建一个AIDL文件
AIDL is a simple syntax that lets you declare an interface with one or more methods, that can take parameters and return values. These parameters and return values can be of any type, even other AIDL-generated interfaces. However, it is important to note that you must import all non-built-in types, even if they are defined in the same package as your interface. Here are the data types that AIDL can support:
AIDL语法简单,你可以用来声明一个带一个或多个方法的接口,也可以传递参数和返回值。这些参数和返回值可以是任何类型,甚至是其他的AIDL生 成的接口。然而,值得重视的是你必须导入所有的non-bult-in类型,即使他们已经作为接口在其他包里定义了。下面是些AIDL支持的数据类型:
Primitive Java programming language types (int, boolean, etc) — No import statement is needed.
简单Java编程语言类型(int,boolean等) -不需要import声明。
One of the following classes (no import statements needed):
下面类之一(不需要import声明)。
.String
.List - All elements in the List must be one of the types in this list, including other AIDL-generated interfaces
and parcelables. List may optionally be used as a "generic" class (e.g. List<String>). The actual concrete class
that the other side will receive will always be an ArrayList, although the method will be generated to use the
List interface.
.List - List中的所有元素都必须是可支持的类型中的一个,包括其他AIDL生成接口和parcelables。List可以作为泛型类来灵活使用(比如
List<String>)。而实际的接受方的类则总是ArrayList,尽管该方法将被生成来使用List接口。
.Map - All elements in the Map must be of one of the types in this list, including other AIDL-generated interfaces
and parcelables. Generic maps, (e.g. of the form Map<String,Integer> are not supported. The actual concrete class
that the other side will receive will always be a HashMap,although the method will be generated to use the Map interface.
.Map - Map中的所有元素都必须是可支持的类型中的一个,包括其他AIDL生成接口和parcelables。泛型化的Maps(比如:Map<Stirng,Integer>)不被支持。
而实际的接受方的类则总是HashMap,尽管该方法将被生成去使用Map接口。
.CharSequence - This is useful for the CharSequence types used by TextView and other widget objects.
.CharSequence - CharSequence的作用是可以被TextView和其他Widget对象使用。
Other AIDL-generated interfaces, which are always passed by reference. An import statement is always needed for these. Custom classes that implement the Parcelable protocol and are passed by value. An import statement is always needed for these.
其他的AIDL生成接口通过引用方式进行传递。所以import声明是必须的。封装协议实现的自定义的类是值传递的方式。所以import声明也是 必须的。
Here is the basic AIDL syntax:
下面是基本的AIDL语法:
// My AIDL file, named SomeClass.aidl
// Note that standard comment syntax is respected.
// Comments before the import or package statements are not bubbled up
// to the generated interface, but comments above interface/method/field
// declarations are added to the generated interface.
// Include your fully-qualified package statement.
package com.google.android.sample;
// See the list above for which classes need
// import statements (hint--most of them)
import com.google.android.sample.IAtmService;
// Declare the interface.
interface IBankAccountService {
// Methods can take 0 or more parameters, and
// return a value or void.
int getAccountBalance();
void setOwnerNames(in List<String> names);
// Methods can even take other AIDL-defined parameters.
BankAccount createAccount(in String name, int startingDeposit, in IAtmService atmService);
// All non-Java primitive parameters (e.g., int, bool, etc) require
// a directional tag indicating which way the data will go. Available
// values are in, out, inout. (Primitives are in by default, and cannot be otherwise).
// Limit the direction to what is truly needed, because marshalling parameters
// is expensive.
int getCustomerList(in String branch, out String[] customerList);
}
[ 编辑 ] 实现接口
AIDL generates an interface file for you with the same name as your .aidl file. If you are using the Eclipse plugin, AIDL will automatically be run as part of the build process (you don't need to run AIDL first and then build your project). If you are not using the plugin, you should run AIDL first.
AIDL生成一个接口文件,文件名和你的AIDL文件名一致。如果你使用的是Eclipse插件,AIDL会作为build过程的一部分自动运行 (你不需要首先运行ADIL然后再去创建你的项目)。否则的话,你需要首先运行AIDL。
The generated interface includes an abstract inner class named Stub that declares all the methods that you declared in your .aidl file. Stub also defines a few helper methods, most notably asInterface(), which takes an IBinder (passed to a client's onServiceConnected() implementation when applicationContext.bindService() succeeds), and returns an instance of the interface used to call the IPC methods. See the section Calling an IPC Method for more details on how to make this cast.
生成的接口包括一个名为Stub的内部抽象类,该类声明了你在aidl文件中声明的所有方法。Stub也定义几个有用的方法,最特别的是 asInterface(),它执行一个IBinder(在 applicationContext.bindService()执行成功后传给客户端onServiceConnected()方法),并返回一个用 来调用IPC方法的接口实例。更多细节请查看章节调用IPC方法。
To implement your interface, extend YourInterface.Stub, and implement the methods. (You can create the .aidl file and implement the stub methods without building between--the Android build process will process .aidl files before .java files.)
实现接口,扩展YourInterface.Stub,并实现方法成员。(你可以创建一个aidl文件并实现stub方法而不用绑定 -AndRoid创建过程在java文件之前会处理aidl文件)。
Here is an example of implementing an interface called IRemoteService, which exposes a single method, getPid(), using an anonymous instance:
这里有个例子,它实现了一个调用IRemoteService的接口,并使用匿名实例公开一个简单的方法gerPid():
// No need to import IRemoteService if it's in the same project.
private final IRemoteService.Stub mBinder = new IRemoteService.Stub(){
public int getPid(){
return Process.myPid();
}
}
A few rules about implementing your interface:
实现接口时有几个原则:
.No exceptions that you throw will be sent back to the caller.
.抛出的异常不要返回给调用者。
.IPC calls are synchronous. If you know that an IPC service takes more than a few milliseconds to complete,
you should not call it in the Activity/View thread, because it might hang the application (Android might display
an "Application is Not Responding" dialog). Try to call them in a separate thread.
.IPC调用是同步的。如果你知道一个IPC服务需要超过几毫秒的时间才能完成地话,你应该避免在Activity/View线程中调用。
因为它会挂起应用程序(AndRoid可能会显示"应用程序没有响应"对话框)。试着在一个独立的线程中调用。
.Only methods are supported; you cannot declare static fields in an AIDL interface.
.只有方法才获得支持;你不能在AIDL接口中声明静态属性。
向客户端公开接口
Now that you've got your interface implementation, you need to expose it to clients. This is known as "publishing your service." To publish a service, inherit Service and implement getBinder() to return an instance of the class that implements your interface. Here's a code snippet of a service that exposes the IRemoteService interface to clients
现在你已完成了接口的实现,你需要向客户端公开该实现。这就是我们所熟悉的"发布服务"。发布一个Service,然后继承Service并实现 getBinder()返回一个实现的类的实例。下面是个Service的代码片断,该Service向客户端公了IRemoteService接口。
public class RemoteService extends Service {
...
@Override
public IBinder getBinder() {
return mBinder;
}
/**
* The IRemoteInterface is defined through IDL
*/
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid() {
return Process.myPid();
}
};
}
使用parcelables进行参数的值传递
Warning: Parcelables currently do not work if you're using the Eclipse plugin. You will see these errors if you try: 警告:如果你现在使用Eclipse插件,Parcelables并不能工作。你会看到以下的错误信息:
.aidl files that only declare parcelables don't need to go in the makefile
.仅声明parcelables的.aidl文件不需要写进makefile
.aidl can only generate code for interfaces, not parcelables
.aidl只能生成接口代码,而不是parcelables。
This is a known limitation. Parcelables can still be used with the ant build.xml files or your own custom build system. A workaround is for you to run the aidl tool by hand for all of your interfaces and add them to your Eclipse project. See step 5 below for why Eclipse shouldn't be trying to compile these aidl files.
这是个众所周知的局限。Parcelables仍然可以被ant build的xml文件或自定义的build系统所使用。你应该在Eclipse项目中添加一个工作区,该工作区可以为所有的接口手动运行aidl工具。 下面的步骤5说明为何Eclipse不该尝试编译这些aidl文件。
If you have a class that you would like to send from one process to another through an AIDL interface, you can do that. You must ensure that the code for your class is available to the other side of the IPC. Generally, that means that you're talking to a service that you started.
如果你有类需要通过AIDL接口从一个进程发送到另一个,你必须确保类代码可以被IPC接收端所使用。通常这意味着一开始你就要和service进 行通讯。
There are five parts to making a class support the Parcelable protocol:
让类支持parcelable协议,有五点需要注意
1.Make your class implement the Parcelable interface.
1. 让类实现Parcelable接口。
2.Implement the method public void writeToParcel(Parcel out) that takes the current state of the object and writes it
to a parcel.
2. 实现public void writeToParcel(Parcel out),该方法可以将当前对象的状态写入parcel.
3.Implement the method public void readFromParcel(Parcel in) that reads the value in a parcel into your object.
3. 实现public void readFromParcel(Parcel in),该方法可以在parcel中读出值到对象中.
4.Add a static field called CREATOR to your class which is an object implementing the Parcelable.Creator interface.
4. 向类中添加一个静态成员,名为CREATOR。该对象实现了Parcelable.Creator接口.
5.Last but not least, add an aidl file for your parcelable class so the AIDL tool can find it, but don't add it to your
build. This file is used like a header file in C. You don't compile the aidl file for a parcelable just like you wouldn't
normally compile a .h file.
5. 向parcelable类中添加一个.aidl文件,以便AIDl工具可以找到。但不要向build中添加该文件。该文件的用法类似于C中的头文件.你不需要为parcelable
编译aidl文件,就像你不会编译个.h文件一样。
AIDL will use these methods and fields in the code it generates to marshall and unmarshall your objects.
AIDL将使用代码中生成的这些方法和成员来伪装或解读对象。
Here is an example of how the Rect class implements the Parcelable protocol.
下面的例子说明了Rect类如何实现了Parcelable协议.
import android.os.Parcel;
import android.os.Parcelable;
public final class Rect implements Parcelable {
public int left;
public int top;
public int right;
public int bottom;
public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect> {
public Rect createFromParcel(Parcel in) {
return new Rect(in);
}
public Rect[] newArray(int size) {
return new Rect[size];
}
};
public Rect() { }
private Rect(Parcel in) {
readFromParcel(in);
}
public void writeToParcel(Parcel out) {
out.writeInt(left);
out.writeInt(top);
out.writeInt(right);
out.writeInt(bottom);
}
public void readFromParcel(Parcel in) {
left = in.readInt();
top = in.readInt();
right = in.readInt();
bottom = in.readInt();
}
}
Here is Rect.aidl for this example
示例的Rect.aidl
package android.graphics;
// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect;
The marshalling in the Rect class is pretty simple. Take a look at the other methods on Parcel to see the other kinds of values you can write to a Parcel.
Rect类中的伪装是相当简单的。仔细看看Parcel中的其他方法,你会看到其他各种值你都可以写进Parcel.
Warning: Don't forget the security implications of receiving data from other processes. In this case, the rect will read four numbers from the parcel, but it is up to you to ensure that these are within the acceptable range of values for whatever the caller is trying to do. See Security and Permissions in Android for more on how to keep your application secure from malware.
警告:不要忽视从其他进程接收数据时的安全性考虑。在本例中,rect将从parcel中读四个数字,而你的工作则是确保这些都在可接受的值得范围 内而不管调用者想要干什么。AndRoid中的安全和访问许可中有更多关于如何确保应用程序安全的信息。
调用一个IPC方法
Here are the steps a calling class should make to call your remote interface:
调用类调用远程接口的步骤:
1.Declare a variable of the interface type that your .aidl file defined.
1. 声明一个接口类型的变量,该接口类型在.aidl文件中定义。
2.Implement ServiceConnection.
2. 实现ServiceConnection。
3.Call ApplicationContext.bindService(), passing in your ServiceConnection implementation.
3. 调用ApplicationContext.bindService(),并在ServiceConnection实现中进行传递.
4.In your implementation of ServiceConnection.onServiceConnected(), you will receive an IBinder instance (called service).
Call YourInterfaceName.Stub.asInterface((IBinder)service) to cast the returned parameter to YourInterface type.
4. 在ServiceConnection.onServiceConnected()实现中,你会接收一个IBinder实例(被调用的Service). 调用
YourInterfaceName.Stub.asInterface((IBinder)service)将参数转换为YourInterface类型。
5.Call the methods that you defined on your interface. You should always trap DeadObjectException exceptions, which are
thrown when the connection has broken; this will be the only exception thrown by remote methods.
5. 调用接口中定义的方法。 你总会捕捉到DeadObjectException异常,该异常在连接断开时被抛出。它只会被远程方法抛出。
6.To disconnect, call ApplicationContext.unbindService() with the instance of your interface.
6. 断开连接,调用接口实例中的ApplicationContext.unbindService()
A few comments on calling an IPC service:
调用IPC服务需要注意几点:
.Objects are reference counted across processes.
.You can send anonymous objects as method arguments.
.匿名对象可以通过方法参数发送。
Here is some sample code demonstrating calling an AIDL-created service, taken from the Remote Activity sample in the ApiDemos project.
下面的代码展示了在ApiDemos项目从远程Activity例子中调用AIDL创建Service的过程。
public class RemoteServiceBinding extends Activity{
IRemoteService mService = null;
Button mKillButton;
private boolean mIsBound;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.remote_service_binding);
// Watch for button clicks.
Button button = (Button)findViewById(R.id.bind);
button.setOnClickListener(mBindListener);
button = (Button)findViewById(R.id.unbind);
button.setOnClickListener(mUnbindListener);
mKillButton = (Button)findViewById(R.id.kill);
mKillButton.setOnClickListener(mKillListener);
mKillButton.setEnabled(false);
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = IRemoteService.Stub.asInterface((IBinder)service);
mKillButton.setEnabled(true);
// As part of the sample, tell the user what happened.
NotificationManager nm = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
nm.notifyWithText(R.string.remote_service_connected,
getText(R.string.remote_service_connected),
NotificationManager.LENGTH_SHORT,
null);
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mKillButton.setEnabled(false);
// As part of the sample, tell the user what happened.
NotificationManager nm = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
nm.notifyWithText(R.string.remote_service_disconnected,
getText(R.string.remote_service_disconnected),
NotificationManager.LENGTH_SHORT,
null);
}
};
private OnClickListener mBindListener = new OnClickListener() {
public void onClick(View v) {
// Establish a connection with the service, by its class name.
bindService(new Intent(RemoteServiceBinding.this,
RemoteService.class),
null, mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
};
private OnClickListener mUnbindListener = new OnClickListener() {
public void onClick(View v) {
if (mIsBound) {
// Detach our existing connection.
unbindService(mConnection);
mKillButton.setEnabled(false);
mIsBound = false;
}
}
};
private OnClickListener mKillListener = new OnClickListener() {
public void onClick(View v) {
// To kill the process hosting our service, we need to know its
// PID. Conveniently our service has a call that will return
// to us that information.
if (mService != null) {
int pid = -1;
try {
pid = mService.getPid();
} catch (DeadObjectException ex) {
// Recover gracefully from the process hosting the
// server dying.
// Just for purposes of the sample, put up a notification.
NotificationManager nm = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
nm.notifyWithText(R.string.remote_call_failed,
getText(R.string.remote_call_failed),
NotificationManager.LENGTH_SHORT,
null);
}
if (pid > 0) {
// Go away!
Process.killProcess(pid);
}
}
}
};
}