Android Interface Definition Language (AIDL)

IN THIS DOCUMENT

  1. Defining an AIDL Interface
    1. Create the .aidl file
    2. Implement the interface
    3. Expose the interface to clients
  2. Passing Objects over IPC
  3. Calling an IPC Method

SEE ALSO

  1. 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必须保证所有线程的安全性。
  • Theonewaykeyword 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 theBinderthread pool as a normal remote call. Ifonewayis 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,基本步骤如下:

  1. 创建.aidl文件

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

  2. 实现接口

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

  3. 公开接口

    实现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. Eitherin,out, orinout(see the example below).

    Primitives areinby default, and cannot be otherwise.

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

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

下面上.aidl文件的例子:

// IRemoteService.aidlpackage 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 sectionCalling an IPC Methodfor 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;  }};

这些代码来自ApiDemos的RemoteService.java.

通过IPC传递对象

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

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

  1. 创建实现Parcelable接口的类。
  2. 实现writeToParcel, 将对象目前的状态写到Pacel中。
  3. 增加一个名字为CREATOR的静态的成员变量,这个CREATOR实现了Parcelable.Creator接口。
  4. 最终,创建一个.aidl文件,声明你的parcelable类。

    如果使用的是自定义的构建过程,不用添加.aidl文件到构建过程。.aidl文件不需要编译。

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

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

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 = newParcelable.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接口:

  1. 导入.aidl文件,放在src/目录下。
  2. 声明一个IBinder接口实例(基于AIDL生成的)。
  3. 实现ServiceConnection.
  4. 调用Context.bindService(),通过ServiceConnection实现。
  5. 在实现onServiceConnected()方法时, 会收到一个IBinder实例 (调用service). 调用YourInterfaceName.Stub.asInterface((IBinder)service)转换成自定义的接口类型
  6. 调用接口中定义的方法。必须经常捕捉DeadObjectException异常,当连接断开的时候这个连接会抛出;这是调用远程方法的时候唯一抛出的异常。
  7. 断开连接,调用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);      }    }  };}

更多相关文章

  1. Android文件保存和读取
  2. android Studio导入source文件
  3. android studio 使用adb 命令传递文件到android 设备的方法
  4. Android 接口定义语言 (AIDL)
  5. Android版本管理解决方法小议
  6. Android NDK开发使用以及so文件生成和注意事项
  7. [转]快速切换Android工程版本的方法
  8. 关于获取Android中文件内容有感

随机推荐

  1. Android(安卓)NumberPicker详细使用
  2. android中自定义的一个可以弹出类似于提
  3. 安卓中颜色种类
  4. Android(安卓)MIME类型与文件后缀名匹配
  5. LinearLayout中设置 水平且垂直 居中
  6. GridView 实现水平拖拉效果
  7. Android(安卓)APK 反编译工具
  8. Android(安卓)改变ImageView图片的Bitmap
  9. android 还原短信
  10. android studio在终端执行gradlew命令,报