Android开发者指南(6) —— AIDL

前言

  本章内容为开发者指南(Dev Guide)/Developing/Tools/aidl,版本为Android2.3 r1,翻译来自"移动云_文斌",欢迎访问它的博客:"http://blog.csdn.net/caowenbin ",再次感谢"移动云_文斌" !期待你一起参与翻译Android的相关资料,联系我[email protected]。

声明

  欢迎转载,但请保留文章原始出处:)

    博客园:http://www.cnblogs.com/

Android中文翻译组: http://goo.gl/6vJQl

原文    http://developer.android.com/guide/developing/tools/aidl.html ( 注意: 3.0 r1 以后移到 Appendix )

正文

  使用 AIDL 设计远程接口 (Designing a Remote Interface Using AIDL)

由于每个应用程序都运行在自己的进程空间,并且可以从应用程序 UI运行另一个服务进程,而且经常会在不同的进程间传递对象。在 Android平台,一个进程通常不能访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的基本单元,并且有序的通过进程边界。

通过代码来实现这个数据传输过程是冗长乏味的, Android提供了 AIDL工具来处理这项工作。

AIDL (Android Interface Definition Language)是一种 IDL 语言,用于生成可以在 Android设备上两个进程之间进行进程间通信 (IPC)的代码。如果在一个进程中(例如 Activity)要调用另一个进程中(例如 Service)对象的操作,就可以使用 AIDL生成可序列化的参数。

AIDL IPC机制是面向接口的,像 COM Corba一样,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。

使用 AIDL 实现 IPC(Implementing IPC Using AIDL)

使用 AIDL实现 IPC服务的步骤是:

1. 创建 .aidl文件 -该文件( YourInterface.aidl)定义了客户端可用的方法和数据的接口。

2. makefile文件中加入 .aidl文件 - Eclipse中的 ADT插件提供管理功能) Android包括名为 AIDL的编译器,位于 tools/文件夹。

3. 实现接口 -AIDL编译器从 AIDL接口文件中利用 Java语言创建接口,该接口有一个继承的命名为 Stub的内部抽象类(并且实现了一些 IPC调用的附加方法),要做的就是创建一个继承于 YourInterface.Stub的类并且实现在 .aidl文件中声明的方法。

4. 向客户端公开接口 -如果是编写服务,应该继承 Service并且重载 Service.onBind(Intent) 以返回实现了接口的对象实例

  创建 .aidl 文件 (Create an .aidl File)

AIDL使用简单的语法来声明接口,描述其方法以及方法的参数和返回值。这些参数和返回值可以是任何类型,甚至是其他 AIDL生成的接口。重要的是必须导入所有非内置类型,哪怕是这些类型是在与接口相同的包中。下面是 AIDL能支持的数据类型:

* Java编程语言的主要类型 (int, boolean ) 不需要 import 语句。

* 以下的类 (不需要 import 语句 ):

String

List -列表中的所有元素必须是在此列出的类型,包括其他 AIDL生成的接口和可打包类型。 List可以像一般的类(例如 List<String>)那样使用,另一边接收的具体类一般是一个 ArrayList,这些方法会使用 List接口。

Map - Map中的所有元素必须是在此列出的类型,包括其他 AIDL生成的接口和可打包类型。一般的 maps(例如 Map<String,Integer>)不被支持,另一边接收的具体类一般是一个 HashMap,这些方法会使用 Map接口。

CharSequence -该类是被 TextView和其他控件对象使用的字符序列。

* 通常引引用方式传递的其他 AIDL生成的接口,必须要 import 语句声明

* 实现了 Parcelable protocol 以及按值传递的自定义类,必须要 import 语句声明。

以下是基本的 AIDL语法:

  

  实现接口 (Implementing the Interface)

AIDL 生成了与 .aidl文件同名的接口,如果使用 Eclipse插件, AIDL会做为编译过程的一部分自动运行(不需要先运行 AIDL再编译项目),如果没有插件,就要先运行 AIDL

生成的接口包含一个名为 Stub的抽象的内部类,该类声明了所有 .aidl中描述的方法, Stub还定义了少量的辅助方法,尤其是 asInterface(),通过它或以获得 IBinder(当 applicationContext.bindService()成功调用时传递到客户端的 onServiceConnected())并且返回用于调用 IPC方法的接口实例,更多细节参见 Calling an IPC Method

要实现自己的接口,就从 YourInterface.Stub类继承,然后实现相关的方法(可以创建 .aidl文件然后实现 stub方法而不用在中间编译, Android编译过程会在 .java文件之前处理 .aidl文件)。

这个例子实现了对 IRemoteService 接口的调用,这里使用了匿名对象并且只有一个 getPid() 接口。

  

   这里是实现接口的几条说明:

* 不会有返回给调用方的异常

* 默认 IPC调用是同步的。如果已知 IPC服务端会花费很多毫秒才能完成,那就不要在 Activity View线程中调用,否则会引起应用程序挂起( Android可能会显示“应用程序未响应”对话框),可以试着在独立的线程中调用。

* AIDL接口中只支持方法,不能声明静态成员。

  向客户端暴露接口 (Exposing Your Interface to Clients)

在完成了接口的实现后需要向客户端暴露接口了,也就是发布服务,实现的方法是继承 Service,然后实现以 Service.onBind(Intent)返回一个实现了接口的类对象。下面的代码片断表示了暴露 IRemoteService接口给客户端的方式。

public class RemoteService extends Service{
...
@Override
public IBinderonBind(Intentintent){
// Selecttheinterfacetoreturn.Ifyourserviceonlyimplements
// asingleinterface,youcanjustreturnitherewithoutchecking
// theIntent.
if (IRemoteService. class .getName().equals(intent.getAction())){
return mBinder;
}
if (ISecondary. class .getName().equals(intent.getAction())){
return mSecondaryBinder;
}
return null ;
}

/**
*TheIRemoteInterfaceisdefinedthroughIDL
*/
private final IRemoteService.StubmBinder = new IRemoteService.Stub(){
public void registerCallback(IRemoteServiceCallbackcb){
if (cb != null )mCallbacks.register(cb);
}
public void unregisterCallback(IRemoteServiceCallbackcb){
if (cb != null )mCallbacks.unregister(cb);
}
};

/**
*Asecondaryinterfacetotheservice.
*/
private final ISecondary.StubmSecondaryBinder = new ISecondary.Stub(){
public int getPid(){
return Process.myPid();
}
public void basicTypes( int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble,StringaString){
}
};

}

  使用可打包接口传递参数 Pass by value Parameters using Parcelables

如果有类想要能过 AIDL在进程之间传递,这一想法是可以实现的,必须确保这个类在 IPC的两端的有效性,通常的情形是与一个启动的服务通信。

这里列出了使类能够支持 Parcelable 4个步骤:【译者注:原文为 5,但列表为 4项,疑为作者笔误】

1. 使该类实现 Parcelabel接口。

2. 实现 public void writeToParcel(Parcel out) 方法,以便可以将对象的当前状态写入包装对象中。

3. 增加名为 CREATOR的构造器到类中,并实现 Parcelable.Creator接口。

4. 最后,但同样重要的是,创建 AIDL文件声明这个可打包的类(见下文),如果使用的是自定义的编译过程,那么不要编译此 AIDL文件,它像 C语言的头文件一样不需要编译。

AIDL会使用这些方法的成员序列化和反序列化对象。

这个例子演示了如何让 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 RectcreateFromParcel(Parcelin){
return new Rect(in);
}

public Rect[]newArray( int size){
return new Rect[size];
}
};

public Rect(){
}

private Rect(Parcelin){
readFromParcel(in);
}

public void writeToParcel(Parcelout){
out.writeInt(left);
out.writeInt(top);
out.writeInt(right);
out.writeInt(bottom);
}

public void readFromParcel(Parcelin){
left
= in.readInt();
top
= in.readInt();
right
= in.readInt();
bottom
= in.readInt();
}
}

  这个是 Rect.aidl 文件。

   序列化 Rect类的工作相当简单,对可打包的其他类型的数据可以参见 Parcel类。

   警告 :不要忘了对从其他进程接收到的数据进行安全检查。在上面的例子中, rect 要从数据包中读取 4 个数值,需要确认无论调用方想要做什么,这些数值都是在可接受的范围之内。想要了解更多的关于保持应用程序安全的内容,可参见 Security and Permissions

  调用 IPC 方法 (Calling an IPC Method)

这里给出了调用远端接口的步骤:

1. 声明 .aidl文件中定义的接口类型的变量。

2. 实现 ServiceConnection

3. 调用 Context.bindService(),传递 ServiceConnection的实现

4. ServiceConnection.onServiceConnected()方法中会接收到 IBinder对象,调用 YourInterfaceName.Stub.asInterface((IBinder)service)将返回值转换为 YourInterface类型

5. 调用接口中定义的方法。应该总是捕获连接被打断时抛出的 DeadObjectException异常,这是远端方法唯一的异常。

6. 调用 Context.unbindService()断开连接

这里是几个调用 IPC服务的提示:

* 对象是在进程间进行引用计数

* 可以发送匿名对象作为方法参数

以下是演示调用 AIDL 创建的服务,可以在 ApiDemos 项目中获取远程服务的示例。 public static class Binding extends Activity{

/** Theprimaryinterfacewewillbecallingontheservice. */
IRemoteServicemService
= null ;
/** Anotherinterfaceweuseontheservice. */
ISecondarymSecondaryService
= null ;
ButtonmKillButton;
TextViewmCallbackText;
private boolean mIsBound;
/**
*Standardinitializationofthisactivity.SetuptheUI,thenwait
*fortheusertopokeitbeforedoinganything.
*/
@Override
protected void onCreate(BundlesavedInstanceState){
super .onCreate(savedInstanceState);
setContentView(R.layout.remote_service_binding);
// Watchforbuttonclicks.
Buttonbutton = (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(
" Notattached. " );
}

/**
*Classforinteractingwiththemaininterfaceoftheservice.
*/
private ServiceConnectionmConnection = new ServiceConnection(){
public void onServiceConnected(ComponentNameclassName,IBinderservice){
// Thisiscalledwhentheconnectionwiththeservicehasbeen
// established,givingustheserviceobjectwecanuseto
// interactwiththeservice.Wearecommunicatingwithour
// servicethroughanIDLinterface,sogetaclient-side
// representationofthatfromtherawserviceobject.
mService = IRemoteService.Stub.asInterface(service);
mKillButton.setEnabled(
true );
mCallbackText.setText(
" Attached. " );

// Wewanttomonitortheserviceforaslongasweare
// connectedtoit.
try {
mService.registerCallback(mCallback);
}
catch (RemoteExceptione){
// Inthiscasetheservicehascrashedbeforewecouldeven
// doanythingwithit;wecancountonsoonbeing
// disconnected(andthenreconnectedifitcanberestarted)
// sothereisnoneedtodoanythinghere.
}

// Aspartofthesample,telltheuserwhathappened.
Toast.makeText(Binding. this ,R.string.remote_service_connected,
Toast.LENGTH_SHORT).show();
}

public void onServiceDisconnected(ComponentNameclassName){
// Thisiscalledwhentheconnectionwiththeservicehasbeen
// unexpectedlydisconnected--thatis,itsprocesscrashed.
mService = null ;
mKillButton.setEnabled(
false );
mCallbackText.setText(
" Disconnected. " );

// Aspartofthesample,telltheuserwhathappened.
Toast.makeText(Binding. this ,R.string.remote_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};

/**
*Classforinteractingwiththesecondaryinterfaceoftheservice.
*/
private ServiceConnectionmSecondaryConnection = new ServiceConnection(){
public void onServiceConnected(ComponentNameclassName,IBinderservice){
// Connectingtoasecondaryinterfaceisthesameasany
// otherinterface.
mSecondaryService = ISecondary.Stub.asInterface(service);
mKillButton.setEnabled(
true );
}

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

private OnClickListenermBindListener = new OnClickListener(){
public void onClick(Viewv){
// Establishacoupleconnectionswiththeservice,binding
// byinterfacenames.Thisallowsotherapplicationstobe
// installedthatreplacetheremoteservicebyimplementing
// thesameinterface.
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 OnClickListenermUnbindListener = new OnClickListener(){
public void onClick(Viewv){
if (mIsBound){
// Ifwehavereceivedtheservice,andhenceregisteredwith
// it,thennowisthetimetounregister.
if (mService != null ){
try {
mService.unregisterCallback(mCallback);
}
catch (RemoteExceptione){
// Thereisnothingspecialweneedtodoiftheservice
// hascrashed.
}
}

// Detachourexistingconnection.
unbindService(mConnection);
unbindService(mSecondaryConnection);
mKillButton.setEnabled(
false );
mIsBound
= false ;
mCallbackText.setText(
" Unbinding. " );
}
}
};

private OnClickListenermKillListener = new OnClickListener(){
public void onClick(Viewv){
// Tokilltheprocesshostingourservice,weneedtoknowits
// PID.Convenientlyourservicehasacallthatwillreturn
// tousthatinformation.
if (mSecondaryService != null ){
try {
int pid = mSecondaryService.getPid();
// Notethat,thoughthisAPIallowsustorequestto
// killanyprocessbasedonitsPID,thekernelwill
// stillimposestandardrestrictionsonwhichPIDsyou
// areactuallyabletokill.Typicallythismeansonly
// theprocessrunningyourapplicationandanyadditional
// processescreatedbythatappasshownhere;packages
// sharingacommonUIDwillalsobeabletokilleach
// other'sprocesses.
Process.killProcess(pid);
mCallbackText.setText(
" Killedserviceprocess. " );
}
catch (RemoteExceptionex){
// Recovergracefullyfromtheprocesshostingthe
// serverdying.
// Justforpurposesofthesample,putupanotification.
Toast.makeText(Binding. this ,
R.string.remote_call_failed,
Toast.LENGTH_SHORT).show();
}
}
}
};

// ----------------------------------------------------------------------
// Codeshowinghowtodealwithcallbacks.
// ----------------------------------------------------------------------

/**
*Thisimplementationisusedtoreceivecallbacksfromtheremote
*service.
*/
private IRemoteServiceCallbackmCallback = new IRemoteServiceCallback.Stub(){
/**
*Thisiscalledbytheremoteserviceregularlytotellusabout
*newvalues.NotethatIPCcallsaredispatchedthroughathread
*poolrunningineachprocess,sothecodeexecutingherewill
*NOTberunninginourmainthreadlikemostotherthings--so,
*toupdatetheUI,weneedtouseaHandlertohopoverthere.
*/
public void valueChanged( int value){
mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG,value,
0 ));
}
};
private static final int BUMP_MSG = 1 ;
private HandlermHandler = new Handler(){
@Override
public void handleMessage(Messagemsg){
switch (msg.what){
case BUMP_MSG:
mCallbackText.setText(
" Receivedfromservice: " + msg.arg1);
break ;
default :
super .handleMessage(msg);
}
}
};
}

更多相关文章

  1. android persistent属性研究
  2. android 问题汇总系列之八
  3. android 6.0sd卡内部存储 & 外部存储
  4. Android——api阅读记录
  5. ActivityManagerService启动流程详解
  6. android中使用代码适配屏幕,dp与px互转、sp与px互转
  7. Mac OS X下搭建Android(安卓)Source编译环境的问题及解决方法
  8. Xposed Android(安卓)hook -方法介绍
  9. Android(安卓)利用程序实现GPS的打开或关闭

随机推荐

  1. Jenkins+Maven+Gitlab+Nexus持续集成环境
  2. Windows环境下Git配置及使用
  3. Oracle ADDM --dbms_addm执行oracle数据
  4. 【DB笔试面试726】在Oracle中,RAC中的Publ
  5. 【DB笔试面试739】在Oracle中,如何获取集
  6. 【DB笔试面试602】在Oracle中,如何从执行
  7. 【DB笔试面试756】在Oracle的DG中,有哪些
  8. Oracle 定义者权限与调用者权限(AUTHID CU
  9. 【DB笔试面试591】在Oracle中,什么是星型
  10. 【DB笔试面试601】在Oracle中,给出下面执