Android SDK开发指南(翻译)系列三:Tools(一)--使用AIDL, 设计一个远程接口
2010-07-26 01:33
1311 查看
这份文档描述如下:
用aidl 实现IPC创建一个.aidl文件
实现该接口
你的接口暴露给客户端
按值传递参数使用Parcelables
调用的IPC方法
由于每个应用程序运行在它自己的进程,你可以写一个服务运行在您的应用程序UI的不同进程时,这个服务有时需要连接进程间的对象。在Android平台,一个进程通常不能访问另一个进程的内存。所以讲,他们需要分解他们的对象成原语,使操作系统可以理解,并跨越边界“marshal”你的对象。marshalling代码编写是乏味的,所以我们提供的AIDL工具来为你做它。
AIDL(Android的接口定义语言)是一个IDL语言,用来生成代码,使Android设备两个进程通过进程间通信(IPC)进行沟通。如果你在一个进程中的代码(例如,在一个Activity),需要在另一个进程调用一个对象的方法(例如,一个Service),您将使用的AIDL来生成代码marshall 参数。
这个AIDL IPC的机制是基于接口的,类似于COM或Corba的,但重量更轻。它使用代理类在客户端与implementation之间传递数值。
用aidl 实现IPC
使用的AIDL按照这些步骤执实现IPC服务。
1.创建您的.aidl文件 - 该文件定义了一个接口(YourInterface.aidl),它定义的方法和字段提供给客户。
2. 添
加.aidl文件到您的makefile - (Eclipse的ADT插件为你管理这个)。Android包括AIDL编译器,在tools/ 目录中。
3. 实现你的接口方法 - AIDL编译器从您的AIDL接口创建一个Java编程语言接口。这个接口有一个内在的抽象的类名为Stub, Stub类继承了这个接口(并实现了一些额外方法,供IPC调用)。你必须创建一个类--extendsYourInterface.Stub,并实现你的.aidl文件中声明的方法。
4. 暴露你的接口给客户的 - 如果你正在编写一个服务,你应该继承服务,重写(override)Service.onBind(Intent)返回您的类的实例,它实现你的接口。
创建一个.aidl文件
AIDL是一个简单的语法,让你声明一个或多个方法的接口,这个方法可以获取参数和返回值。这些参数和返回值可以是任何类型,甚至其他AIDL生成的接口。不过,重要的是你必须要注意import所有non-built-in类型,即使他们是定义在同一个包中的接口。下面是AIDL可以支持的数据类型:
•原始Java编程语言类型(int,boolean等) -可以不用import语句。
•以下类之一(不需要.import):
O String
O List - 列表中的所有元素都必须是在此列表类型,包括其他AIDL生成接口和parcelables。名单可以选择作为一个“generic”类(如List<String>)。实际的具体类,其他方将获得将永远是一个ArrayList,尽管该方法将产生使用List接口。
O Map - 在Map的所有元素必须是在这列表类型,包括其他AIDL生成接口和parcelables。通用Map,(如格式Map<String,Integer>)不支持的。实际的具体类,其他方将获得永远是一个HashMap,尽管该方法将产生使用的地图接口。
O CharSequence - 这非常有用, 因为CharSequence类型是由TextView和其他widget对象所使用的。
•其他AIDL生成的接口,它总是通过引用传递。需要import语句。
•自定义类实现Parcelable协议,并通过值传递。需要import语句。
这里是基本的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.android.sample; // See the list above for which classes need // import statements (hint--most of them) import com.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为您的接口文件生成具有相同名称的.aidl文件。如果您使用的是Eclipse的插件,将自动运行的AIDL作为build过程(不需要先运行的AIDL,再build项目)。如果你不使用插件,你应该先运行AIDL。
生成的接口包括一个抽象的内部类名为Stub,声明所有的方法----所有您在.aidl文件声明。Stub还定义了一些辅助方法,最值得注意的如:asInterface(),它采用一个IBinder(当applicationContext.bindService()成功, 传递给客户端的onServiceConnected()执行),并返回一个用于调用IPC方法的接口的实例。要了解更多细节, 请查看Calling an IPC Method部分,。
要实现你的接口,扩展YourInterface.Stub,实现方法。(您可以创建.aidl文件和执行stub方法而不需要build----Android的build过程中会在java文件之前处理.aidl文件)
这里是一个接口实现的例子,称为IRemoteService,它使用一个匿名实例公开一个方法----getPid():
// 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(); } }
关于实现你的接口有几个规则:
•没有抛出异常,你将被发送回调用方。
•默认情况下,IPC的调用是同步的。如果你知道的IPC服务需要超过几毫秒的时间完成,你不应该它在Activity/View线程调用,因为它可能会挂起应用程序(Android可能会显示一个“应用程序没有响应”对话框)。尝试在一个单独的线程调用它们。
•只有支持的方法可用,你不能AIDL接口中声明一个静态字段。
接口暴露给客户端
现在你有你的接口实现,您需要暴露给客户端。这就是所谓的“发布您服务”。要发布一个服务,继承服务和实现Service.onBind(Intent) 以返回一个类的实例,这个类实现了你的接口。下面是一个Service代码段,暴露IRemoteService接口给客户端。
public class RemoteService extends Service { ... @Override public IBinder onBind(Intent intent) { // Select the interface to return. If your service only implements // a single interface, you can just return it here without checking // the Intent. if (IRemoteService.class.getName().equals(intent.getAction())) { return mBinder; } if (ISecondary.class.getName().equals(intent.getAction())) { return mSecondaryBinder; } return null; } /** * The IRemoteInterface is defined through IDL */ private final IRemoteService.Stub mBinder = new IRemoteService.Stub() { public void registerCallback(IRemoteServiceCallback cb) { if (cb != null) mCallbacks.register(cb); } public void unregisterCallback(IRemoteServiceCallback cb) { if (cb != null) mCallbacks.unregister(cb); } }; /** * A secondary interface to the service. */ private final ISecondary.Stub mSecondaryBinder = new ISecondary.Stub() { public int getPid() { return Process.myPid(); } public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) { } }; }
使用Parcelables按值传递参数
如果你有一个类,你想通过AIDL接口从一个进程发送到另一个,你可以做到这一点。你必须确保你的类的代码能提供给IPC的另一面。一般来说,这意味着你在跟谁说话,你的服务开始了。
有5个部分,使一个类支持Parcelable协议:
1. 让你的类实现了Parcelable接口。
2. 实现方法public void
writeToParcel(Parcel out) ,是以对象的当前状态写入了一个parcel。value in a parcel into
your object.。
3. 添加一个static字段称为CREATOR的类,是一个对象实现Parcelable.Creator接口。
4. 最后但并非最不重要的,创建一个aidl文件,声明你的parcelable类(如下所示)。如果您使用的是自定义生成过程中,不添加aidl文件到您的构建。类似的C头文件,该aidl文件是没有编译。
AIDL将使用这些方法与字段在代码中以产生marshall and unmarshall对象。
下面是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
package android.graphics; // Declare Rect so AIDL can find it and knows that it implements // the parcelable protocol. parcelable Rect;
在Rect类marshalling是相当简单。看看Parcel其他方法,其他类型的值你可以写一个Parcel。
警告:不要忘记从其他进程接收数据的安全问题。在这种情况下,矩形会从parcel读四个数字,但是无论调用者怎么做,您应确保这些值在可接受的范围。请查看更多关于安全性和权限相关文档,使您的应用程序免受恶意软件的侵害。
调用的IPC方法
下面是calling class调用远程接口步骤:
1. 声明一个您的.aidl文件中定义的接口类型的变量。
2. 实现ServiceConnection。
3. 调用Context.bindService(),通过在你的ServiceConnection实现。
4. 在您的ServiceConnection.onServiceConnected()实现中,您将收到一IBinder实例(称为Service)。
CallYourInterfaceName.Stub.asInterface((IBinder)service)返回你的参数至YourInterface类型。
5. 调用您在接口中定义的方法。你应该总是trap
DeadObjectException异常,这在连接失败时抛出,这将是远程方法抛出的唯一的异常。
6. 要断开,请与您接口实例调用Context.unbindService()。
在调用一个IPC的服务提出几点意见:
•对象被跨进程引用计数。
•您可以发送匿名对象作为方法参数。
下面是一些示例代码演示调用一个的AIDL创建的服务,为ApiDemos例程中Remote Service 样本。
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); } } }; }
相关文章推荐
- Android SDK开发指南(翻译)系列一:最佳实践(二)-- 反应速度设计
- Android - 使用AIDL设计远程接口
- 使用AIDL(Android接口描述语言)设计和使用远程接口
- 一个简单的demo学习Android远程Service(AIDL的使用)
- Android Interface Definition Language (AIDL) android接口定义语言 开发文档翻译 - 2
- Android开发之使用AIDL和远程服务实现进程通信
- Android SDK开发指南(翻译)系列一:最佳实践(一)-- 性能设计
- Android开发周报:50个Android Studio使用秘诀、高质量SDK设计详解
- 使用AIDL设计远程接口(Designing a Remote Interface Using AIDL)
- 使用AOP开发android 远程调用SDK
- 一个简单的demo学习Android远程Service(AIDL的使用
- android开发专题系列-一个简单的游戏的设计
- Xamarin Getting Started翻译系列十--演练—使用Android设计器
- Android Interface Definition Language (AIDL) android接口定义语言 开发文档翻译 - 1
- 一个简单的demo学习Android远程Service(AIDL的使用)
- 一个简单的demo学习Android远程Service(AIDL的使用)
- 使用AIDL设计远程接口(Designing a Remote Interface Using AIDL)
- 使用AIDL(AndRoid接口描述语言)设计和使用远程接口
- 一个简单的demo学习Android远程Service(AIDL的使用)
- 翻译一个关于Android游戏开发的系列文章---1-The Game Idea