您的位置:首页 > 移动开发 > Android开发

Android Interface Definition Language (AIDL)

2013-04-08 11:11 495 查看


Android Interface Definition Language (AIDL)


IN THIS DOCUMENT

Defining an AIDL Interface

Create the .aidl file
Implement the interface
Expose the interface to clients

Passing Objects over IPC
Calling an IPC Method


SEE ALSO

Bound Services

AIDL (Android Interface Definition Language) 与其他的接口定义语言相似。定义这个接口,client 和service 可以通过这个接口在进程之间进行通信。所以说,对象要能在进程之间传递,就需要将对象分解成系统能识别的指令。通过代码实现那个是非常的冗长乏味的,android 系统通过AIDL可以处理。

Note: 希望从不同的应用中访问service,并且在service需要处理多线程,这种情况下才会使用AIDL。如果不需要从其他的应用中处理IPC,那么可以实现Binder;如果需要IPC,但是不需要处理多线程,那么可以使用Messenger。在使用之前,先考虑好哪种方式是适合自己的。

设计AIDL接口之前,需要确认AIDL接口里面该有的方法。不能假设当调用发生时,thread会伴随。发生什么是不同的,取决于该调用是从本地进程或远程进程的线程发起的。具体来讲:

在本地进程中的同一个线程发起调用请求。如果这是你的主UI线程,线程继续在AIDL接口上执行。如果是其他的线程,它会在service中运行代码。如果仅仅是本地线程访问service,可以控制具体的线程在service中执行(如果是在这种情况下,不需要使用AIDL,可以用Binder方式代替).
从远程进程的线程池中发起调用。需要准备好处理不知线程的同时发起的调用。换句话说,实现AIDL必须保证所有线程的安全性。
The
oneway
keyword modifies the behavior of remote calls. When used, a remote call does not block; it simply sends the transaction data and immediately
returns. The implementation of the interface eventually receives this as a regular call from the
Binder
thread
pool as a normal remote call. If
oneway
is used with a local call, there is no impact and the call is still synchronous.


Defining an AIDL Interface

可以用Java编程语言的语法定义AIDL接口,文件保存在源代码(src/)下,当前应用拥有这个service并且,其他的应用可以绑定这个service。

当构建的应用中含有.aidl文件,Android SDK 工具根据.aidl文件能够生成一个IBinder的接口,保存在项目的gen/目录。service应该合理的实现IBinder接口。client 应用可以绑定这个service,并通过IBinder调用service的方法。

用AIDL创建一个绑定的service,基本步骤如下:

创建.aidl文件
这个文件定义了接口和方法.

实现接口
Android SDK 工具基于.aidl文件能够生成一个接口。接口里面含有一个名字为Stub的抽象内部类,并且实现了AIDL接口中定义的方法。使用的时候,必须继承Stub类,实现它的方法。

公开接口
实现service,重写onBind(), 它的返回值是stub类。

Caution: 对AIDL接口做的任何改变,必须向后兼容,避免其他使用service的应用无法继续工作。也就是说,因为这个.aidl文件必须复制到其他的应用中,为了能访问这个service的接口,必须保证对原始接口的支持。


1. 创建.aidl文件

AIDL 使用简单的语法定义接口,可以有一个或者多个方法,这些方法可以传入参数和返回值。参数和返回值可以是任何类型,甚至其他aidl生成的接口。

.aidl文件必须使用java语法,每一个.aidl文件只能定义一个单独的接口,并且只需要接口的声明和方法的声明。

一般情况,AIDL支持一下数据类型:

所有的java基本类型 (例如
int
,
long
,
char
,
boolean
,
等等)
String

CharSequence

List

List中所有的元素必须是AIDL支持的类型,或者是其他的AIDL接口和定义的parcebles.一个List可以被用来作为一个通常类(例如,List<String>). 实际具体的类接到总是一个ArrayList,虽然该方法生成的时候是List 接口.

Map

Map中所有的元素必须是AIDL支持的类型,或者是其他的AIDL接口和定义的parcebles.通常的Map(例如Map<String,Integer>这种形式的Map是不支持的)。实际具体的类接到总是一个HashMap,虽然该方法生成的时候是Map
接口.

当要导入的类型,上面没有列举,需要import, 即使它们定义在和当前AIDL文件相同的包里面。

当定义service的接口时,要意识到:

方法可以有0个或者多个参数,可以返回一个值或者是void.
All non-primitive parameters require a directional tag indicating which way the data goes. Either
in
,
out
,
or
inout
(see the example below).
Primitives are
in
by default, and cannot be otherwise.

Caution: 必须考虑真正需要的数据方向,因为数据装换是非常昂贵的。

包含在.aidl中所有的注释在IBinder接口中都会生成(除了在import和package之前的注释)
仅仅支持方法,不支持静态的成员变量。

下面上
.aidl
文件的例子:

// IRemoteService.aidl
package com.example.android;

// Declare any non-default types here with import statements

/** Example service interface */
interface IRemoteService {
/** Request the process ID of this service, to do evil things with it. */
int getPid();

/** Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}


将.aidl文件保存在
src/
目录下,当编译项目的时候,, SDK tools 生成IBinder接口文件,放在项目的
gen/
目录里面.

如果您使用的是Eclipse,会迅速的生成Binder类。如果您没有使用Eclipse,Ant tool 可以在下次构建应用的时候,自动生成binder 类-构建项目的时候需要ant debug (或者ant release), 在写好.aidl文件的时候就构建一个次,以便在编码的时候可以引用生成的类。


2. 实现接口

当您构建项目的时候,Android SDK 工具会生成一个java 接口文件。 生成的接口文件中包含一个Stub 的内部抽象类,这个类实现在.aidl中定义的方法。

Note:
Stub
同样定义了一些帮组的方法,最特别的是
asInterface()
, 它需要一个IBinder(通常传递给客户的onServiceConnected()回调方法),
并且返回一个stub接口的实例。See the section Calling
an IPC Method for more details on how to make this cast.

要实现从.aidl文件中生成出来的接口,继承生成的Binder接口(例如,YourInterface.Stub),并且实现从.aidl文件中继承来的方法。

这里是一个通过匿名类的方式实现
IRemoteService
接口的例子:

private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public int getPid(){
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};


现在mBinder是Stub类的一个实例,下一步,这个接口需要暴露给client调用。

这里是实现AIDL接口的一些规则:

不能保证是从主线程里发起的调用,因此在使用的时候,需要考虑多线程启动和保证service运行时的线程安全性。
默认情况,远程调用是同步的。如果你知道你的service完成的任务需要一些时间,不能从activity的主线程中调用service,因为这样调用会导致application挂起(应用等待响应),最好在一个新的线程中调用。
Service不会返回任何开发者自己抛出的异常到调用者。


3. 把接口公开给客户端

一旦service实现了接口,然后要把它暴露给client,以便clients绑定它。为service公开接口,继承Service和实现onBind()并返回实现Stub类的实例.下面的service例子公开
IRemoteService
接口给clients.

public class RemoteService extends Service {
@Override
public void onCreate() {
super.onCreate();
}

@Override
public IBinder onBind(Intent intent) {
// Return the interface
return mBinder;
}

private final IRemoteService.Stub mBinder = new IRemoteService.Stub() { public int getPid(){ return Process.myPid(); } public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) { // Does nothing } };
}


现在,当client调用bindService()绑定service,Client通过onServiceConnected()回调函数获得service
onBind()方法的mBinder实例。

client可以访问接口类,如果client和service在不同的application中,那么client应用必须拷贝.aidl文件到自己的src/目录下。(生成
android.os.Binder
接口—提供给client访问AIDL 方法).

当client从
onServiceConnected()
中获取到IBinder对象,就需要调用
YourServiceInterface.Stub.asInterface(service)
,将返回的参数转换成自己的
ServiceInterface
.
例如:

IRemoteService mIRemoteService;
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
// Following the example above for an AIDL interface,
// this gets an instance of the IRemoteInterface, which we can use to call on the service
mIRemoteService = IRemoteService.Stub.asInterface(service);
}

// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "Service has unexpectedly disconnected");
mIRemoteService = null;
}
};

这些代码来自 ApiDemosRemoteService.java
类.


通过IPC传递对象

如果想用通过IPC接口打对象从一个进程传递到另外一个进程中,可以用这种方式。实现的时候,要注意,为了确保类可以在另外一端的IPC通道中正确运行,这个类需要实现Parcelable接口。实现了Parcelable接口可以保证对象可以正确的被分解成android系统能够识别的信息,并且可以正确的在进程间传输。

为了创建一个类能够支持Parcelable协议,要遵循以下步骤:

创建实现Parcelable接口的类。
实现
writeToParcel
,
将对象目前的状态写到Pacel中。
增加一个名字为CREATOR的静态的成员变量,这个CREATOR实现了Parcelable.Creator接口。
最终,创建一个.aidl文件,声明你的parcelable类。
如果使用的是自定义的构建过程,不用添加.aidl文件到构建过程。.aidl文件不需要编译。

AIDL用这些方法和成员变量可以封装和分解对象。

例如,用
Rect.aidl
文件创建
Rect
parcelable类:

package android.graphics;

// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect;


这里是
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();
}
}


在Rect 类中的封装是相当简单。通过Parcel可以知道它可以写其他的类型到parcel。

Warning: 从其他的进程中收到数据要考虑安全性。在这种情况下, Rect 从Parcel中读取四个值,但是,当调用者调用的时候,必须确保这些值是在可接受的范围之内,可以查看Security
and Permissions内容,了解更加详细的信息,保证应用免受恶意软件的干扰。


调用IPC方法

下面的步骤是调用者如何调用远程的AIDL接口:

导入.aidl文件,放在src/目录下。
声明一个IBinder接口实例(基于AIDL生成的)。
实现
ServiceConnection
.
调用
Context.bindService()
,通过ServiceConnection实现。
在实现
onServiceConnected()方法时
,
会收到一个
IBinder
实例 (调用
service
).
调用
YourInterfaceName.Stub.asInterface((IBinder)service)
转换成自定义的接口类型

调用接口中定义的方法。必须经常捕捉
DeadObjectException
异常,当连接断开的时候这个连接会抛出;这是调用远程方法的时候唯一抛出的异常。
断开连接,调用
Context.unbindService()


对调用IPC service 时候的一些注释:

引用计数的对象是跨进程的。
可以将匿名的对象作为参数。

其他的绑定service的信息,看Bound
Services 文档。

下面是调用AIDL,这些代码在ApiDemos 项目。

public static class Binding extends Activity {
/** The primary interface we will be calling on the service. */
IRemoteService mService = null;
/** Another interface we use on the service. */
ISecondary mSecondaryService = null;

Button mKillButton;
TextView mCallbackText;

private boolean mIsBound;

/**
* Standard initialization of this activity.  Set up the UI, then wait
* for the user to poke it before doing anything.
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

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);

mCallbackText = (TextView)findViewById(R.id.callback);
mCallbackText.setText("Not attached.");
}

/**
* Class for interacting with the main interface of the service.
*/
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(service);
mKillButton.setEnabled(true);
mCallbackText.setText("Attached.");

// We want to monitor the service for as long as we are
// connected to it.
try {
mService.registerCallback(mCallback);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}

// As part of the sample, tell the user what happened.
Toast.makeText(Binding.this, R.string.remote_service_connected,
Toast.LENGTH_SHORT).show();
}

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);
mCallbackText.setText("Disconnected.");

// As part of the sample, tell the user what happened.
Toast.makeText(Binding.this, R.string.remote_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};

/**
* Class for interacting with the secondary interface of the service.
*/
private ServiceConnection mSecondaryConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// Connecting to a secondary interface is the same as any
// other interface.
mSecondaryService = ISecondary.Stub.asInterface(service);
mKillButton.setEnabled(true);
}

public void onServiceDisconnected(ComponentName className) {
mSecondaryService = null;
mKillButton.setEnabled(false);
}
};

private OnClickListener mBindListener = new OnClickListener() {
public void onClick(View v) {
// Establish a couple connections with the service, binding
// by interface names.  This allows other applications to be
// installed that replace the remote service by implementing
// the same interface.
bindService(new Intent(IRemoteService.class.getName()),
mConnection, Context.BIND_AUTO_CREATE);
bindService(new Intent(ISecondary.class.getName()),
mSecondaryConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
mCallbackText.setText("Binding.");
}
};

private OnClickListener mUnbindListener = new OnClickListener() {
public void onClick(View v) {
if (mIsBound) {
// If we have received the service, and hence registered with
// it, then now is the time to unregister.
if (mService != null) {
try {
mService.unregisterCallback(mCallback);
} catch (RemoteException e) {
// There is nothing special we need to do if the service
// has crashed.
}
}

// Detach our existing connection.
unbindService(mConnection);
unbindService(mSecondaryConnection);
mKillButton.setEnabled(false);
mIsBound = false;
mCallbackText.setText("Unbinding.");
}
}
};

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 (mSecondaryService != null) {
try {
int pid = mSecondaryService.getPid();
// Note that, though this API allows us to request to
// kill any process based on its PID, the kernel will
// still impose standard restrictions on which PIDs you
// are actually able to kill.  Typically this means only
// the process running your application and any additional
// processes created by that app as shown here; packages
// sharing a common UID will also be able to kill each
// other's processes.
Process.killProcess(pid);
mCallbackText.setText("Killed service process.");
} catch (RemoteException ex) {
// Recover gracefully from the process hosting the
// server dying.
// Just for purposes of the sample, put up a notification.
Toast.makeText(Binding.this,
R.string.remote_call_failed,
Toast.LENGTH_SHORT).show();
}
}
}
};

// ----------------------------------------------------------------------
// Code showing how to deal with callbacks.
// ----------------------------------------------------------------------

/**
* This implementation is used to receive callbacks from the remote
* service.
*/
private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
/**
* This is called by the remote service regularly to tell us about
* new values.  Note that IPC calls are dispatched through a thread
* pool running in each process, so the code executing here will
* NOT be running in our main thread like most other things -- so,
* to update the UI, we need to use a Handler to hop over there.
*/
public void valueChanged(int value) {
mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0));
}
};

private static final int BUMP_MSG = 1;

private Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case BUMP_MSG:
mCallbackText.setText("Received from service: " + msg.arg1);
break;
default:
super.handleMessage(msg);
}
}

};
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: