Android SDK开发指南(翻译)系列三:Tools(一)--使用AIDL, 设计一个远程接口

 

这份文档描述如下:

  用aidl 实现IPC

    创建一个.aidl文件

    实现该接口

    你的接口暴露给客户端

    按值传递参数使用Parcelables

  调用的IPC方法

 

    由于每个应用程序运行在它自己的进程,你可以写一个服务运行在您的应用程序UI的不同进程时,这个服务有时需要连接进程间的对象。在Android平台,一个进程通常不能访问另一个进程的内存。所以讲,他们需要分解他们的对象成原语,使操作系统可以理解,并跨越边界“marshal”你的对象。marshalling代码编写是乏味的,所以我们提供的AIDL工具来为你做它。

    AIDLAndroid的接口定义语言)是一个IDL语言,用来生成代码,使Android设备两个进程通过进程间通信(IPC)进行沟通。如果你在一个进程中的代码(例如,在一个Activity),需要在另一个进程调用一个对象的方法(例如,一个Service),您将使用的AIDL来生成代码marshall 参数。

    这个AIDL IPC的机制是基于接口的,类似于COMCorba的,但重量更轻。它使用代理类在客户端与implementation之间传递数值。

 

aidl 实现IPC

使用的AIDL按照这些步骤执实现IPC服务。

1.创建您的.aidl文件 - 该文件定义了一个接口(YourInterface.aidl),它定义的方法和字段提供给客户。

2. 添 加.aidl文件到您的makefile - EclipseADT插件为你管理这个)。Android包括AIDL编译器,在tools/ 目录中。

3. 实现你的接口方法 - AIDL编译器从您的AIDL接口创建一个Java编程语言接口。这个接口有一个内在的抽象的类名为Stub, Stub类继承了这个接口(并实现了一些额外方法,供IPC调用)。你必须创建一个类--extendsYourInterface.Stub,并实现你的.aidl文件中声明的方法。

4. 暴露你的接口给客户的 - 如果你正在编写一个服务,你应该继承服务,重写(override)Service.onBindIntent)返回您的类的实例,它实现你的接口。

 

创建一个.aidl文件

    AIDL是一个简单的语法,让你声明一个或多个方法的接口,这个方法可以获取参数和返回值。这些参数和返回值可以是任何类型,甚至其他AIDL生成的接口。不过,重要的是你必须要注意import所有non-built-in类型,即使他们是定义在同一个包中的接口。下面是AIDL可以支持的数据类型:

•原始Java编程语言类型(intboolean等) -可以不用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----Androidbuild过程中会在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) ,是以对象的当前状态写入了一个parcelvalue 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;

  在Rectmarshalling是相当简单。看看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);
           
}
       
}

   
};
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值