首页 > Android, Other > AIDL简介

AIDL简介

2010-01-03 21:34 星期日    浏览: 1,344    绿 发表评论 阅读评论

AIDL(Android Interface Definition Language,Android接口定义语言)是一种接口定义语言(IDL,Interface Definition Language),用于生成进程间通讯所需的代码。假如一个应用程序(如Android中的activity)需要用到另一个程序中的服务,如需要调用某个service中某个对象的成员函数,你需要用到AIDL去生成相关的代码。
AIDL通讯机制是基于接口的。类似于COM和CORBA,但是轻量级。在客户端,它使用一个代理(proxy)去完成与服务端的通讯。

使用AIDL实现IPC通讯主要有以下4步:
step1:创建.aidl文件
aidl文件用来申明一个接口,接口中的函数可以使用各种数据类型,甚至另一个aidl中申明的接口类型。当然,对于那些非内建的、自己定义的数据类型,需要使用import导入,才可正常使用。
下面是一个来自于android中音乐播放器中的aidl文件(packages/apps/Music/src/com/android/music/IMediaPlaybackService.aidl
),它声明了一个接口,这些接口将在播放器程序中得到调用。实际执行者是server端提供的相应的服务。

package com.android.music;
import android.graphics.Bitmap;
interface IMediaPlaybackService
{
    void openFile(String path, boolean oneShot);
    void openFileAsync(String path);
    void open(in int [] list, int position);
    int getQueuePosition();
    boolean isPlaying();
    void stop();
    void pause();
    void play();
    void prev();
    void next();
    long duration();
    long position();
    long seek(long pos);
    String getTrackName();
    String getAlbumName();
    int getAlbumId();
    String getArtistName();
    int getArtistId();
    void enqueue(in int [] list, int action);
    int [] getQueue();
    void moveQueueItem(int from, int to);
    void setQueuePosition(int index);
    String getPath();
    int getAudioId();
    void setShuffleMode(int shufflemode);
    int getShuffleMode();
    int removeTracks(int first, int last);
    int removeTrack(int id);
    void setRepeatMode(int repeatmode);
    int getRepeatMode();
    int getMediaMountedCount();
}

step2:将aidl文件添加到makefile文件中。在Eclipse集成开发环境中,这一步不需要,因为Android提供的开发插件ADT已经帮我们做了这一步。

step3:为接口提供实现。这一步是在server侧(通常就是在各种service中)实现这些接口。这样,client侧(通常就是我们的Java应用程序)就可以用到它们的提供的服务。在Eclipse开发环境中,ADT将自动生成一个与你的aidl文件同名的Java接口文件,在编译完整个源码后,见文件out/target/common/obj/APPS/Music_intermediates/src/src/com/android/music/IMediaPlaybackService.java。

它的整个文件代码层次如下:先包含一个Stub抽象嵌套类,在Stub中又有一个Proxy嵌套类。在整个IMediaPlaybackService接口的最后,才是各个接口的声明。在Proxy类中,依次实现了这些接口的同名函数。在Stub的onTransact函数中,又用switch…case的形式为实现了分支选择,case恒量如下面的“TRANSACTION_”为前缀的Java恒量。

public interface IMediaPlaybackService extends android.os.IInterface
{
public static abstract class Stub extends android.os.Binder implements com.android.music.IMediaPlaybackService
{
      //…
     private static class Proxy implements com.android.music.IMediaPlaybackService
     {//…}
   static final int TRANSACTION_openfile = (IBinder.FIRST_CALL_TRANSACTION + 0);
   static final int TRANSACTION_openfileAsync = (IBinder.FIRST_CALL_TRANSACTION + 1);
    //…
 }// class Stub
public void openfile(java.lang.String path) throws android.os.RemoteException;
public void openfileAsync(java.lang.String path) throws android.os.RemoteException;
public void open(int[] list, int position) throws android.os.RemoteException;
    //…
}// interface IMediaPlaybackService

在这个接口文件中,有一个内嵌的抽象类Stub,Stub中包含了所有在aidl中声明的接口,以及其它一些辅助函数,如asInterface()。该函数有一个IBinder参数,返回接口IMediaPlaybackService的实例。
在该文件中,还有个实现接口的子类Proxy(见319行,它嵌套在Stub类中)。
private static class Proxy implements com.android.music.IMediaPlaybackService
注意:进程间的IPC调用是同步的。因此,如果服务的处理时间过长,则不能在 Activity/View的线程中对它们进行调用,否则会出现”Application is Not Responding” 的对话框,应在另一个线程里进行调用。
另外,AIDL文件中不可声明静态变量。

step4:向clients程序发布接口。为了让Java应用程序可以使用自己的接口调用服务,需要发布自己的接口。这就需要写一个Service的子类,并在这个子类中实现成员函数Service.onBind(Intent) ,返回Stub类的实例。(TODO:上面的IMediaPlaybackService所在的服务的代码)
下面的例子来自于SDK文档。它实现了一个RemoteService,该服务支持两个接口类,分别是IRemoteInterface和ISecondary。在其onBind函数中通过判断,返回接口实例;实例则是
该服务在后面的代码中new出来的。根据需要在为接口的嵌套类Stub添加新的成员函数后,即可创建它们的实例,通过onBind函数返回该实例。

public class RemoteService extends Service {

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

如何在AIDL的IPC机制传递自定义数据类型?
如果你正在使用AIDL定义的接口,但同时需要向远端传递一个自定义数据类型,那该怎么做呢?这就需要server和client侧都支持这种自定义的数据类型。Android已经提供了一种机制去简化这种操作,这就是Parcelable 协议。我们只需为自定义的数据类型中添加如下4步:
step1:让你的自定义数据类型实现Parcelable接口。在声明类时,后面加上implements Parcelable即可
step2:实现该接口的成员函数public void writeToParcel(Parcel out)和 public void readFromParcel(Parcel in) 。它们是往Parcel里写和读你的自定义类中的数据。
step3:添加一个 静态变量CREATOR,该静态变量实现了接口Parcelable.Creator
step4:在你的aidl文件中添加该类。这样AIDL就可以知道它支持Parcel协议。
下面的Rect例子来自于SDK。Rect类通过两个点的坐标定义了一个矩形,它实现了Parcelable接口,实现了writeToParcel和readFromParcel两个方法,有一个CREATOR静态变量域。
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();
    }

在它的aidl中应该有如下行:
// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect;

调用IPC方法:
public class RemoteServiceBinding extends Activity {
    //声明要使用的接口
    IRemoteService mService = null;
    ISecondary mSecondaryService = null;
    Button mKillButton;
    TextView mCallbackText;
    private boolean mIsBound;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.remote_service_binding);
        // 为各按钮设定点击事件的监听者,这样点击时就有反应,并设定它们的初始状态。
        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.”);
    }
//为使用服务中的第一接口所支持的功能,创建一个连接。在连接上后获取服务中接口的实例
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            mService = IRemoteService.Stub.asInterface(service);
            mKillButton.setEnabled(true);
            mCallbackText.setText(“Attached.”);
            try {
               //调用远端服务的接口函数,指定一个回调函数
                mService.registerCallback(mCallback);
            } catch (RemoteException e) {//… }
            //…    
        }
        public void onServiceDisconnected(ComponentName className) {
            mService = null;
            mKillButton.setEnabled(false);
            mCallbackText.setText(“Disconnected.”);
            //…
        }
};
//为使用服务中第二个接口所支持的功能,创建一个连接,获取接口实例
    private ServiceConnection mSecondaryConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            mSecondaryService = ISecondary.Stub.asInterface(service);
            mKillButton.setEnabled(true);
        }

        public void onServiceDisconnected(ComponentName className) {
            mSecondaryService = null;
            mKillButton.setEnabled(false);
        }
    };
//“Bind”按钮添加监听器,一旦被点击,执行下面的操作,去连接远端服务
    private OnClickListener mBindListener = new OnClickListener() {
        public void onClick(View v) {
//使用上面的两个连接变量去绑定远端服务。当连接上后就执行上面连接中的//onServiceConnected函数
            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) {//… }
    };
//Kill按钮监听器,用于杀死服务端所在进程
    private OnClickListener mKillListener = new OnClickListener() {
        public void onClick(View v) {
            if (mSecondaryService != null) {
                try {
                    //此处使用远端服务,获取远端服务所在进程号
                    int pid = mSecondaryService.getPid();
                    Process.killProcess(pid);
                    mCallbackText.setText(“Killed service process.”);
                } catch (RemoteException ex) {//…  }
            }
        }//onClick
    };
    /* 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));
        }
    };//Stub
    private static final int BUMP_MSG = 1;
    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case BUMP_MSG:
                    mCallbackText.setText(“Received from service: ” + msg.arg1);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };//new Handler
}
上述代码功能是:有三个按钮,“Bind”按钮会用两个连接变量去绑定服务,一旦绑定完成,连接建立成功,则执行连接的onConnected函数,获取aidl中声明的两个接口在本地的引用,于是本地使用远端的服务时,就可以像使用本地的方法一样去调用接口中声明的方法。第一个接口在连接成功后,即调用远端的服务。第二个接口则是在“Kill”按钮中午执行远端服务,获取其进程号。第三个按钮“unbind”用于解除绑定,拆除连接。
可见,通过IPC调用远端服务的接口,有如下步骤:
step1:声明一个在你的aidl文件中定义的接口类型的变量。如上例的mService和mSecondaryService,见3、4行
step2:实现ServiceConnection,见23~41行,在其onConnected函数中将通过YourInterface.Stub.asInterface()函数获取本地的YourInterface实例。见行26和行47
step3:调用 Context.bindService(),绑定服务,与服务建立连接。见61~64行
step4:到这一步,你就可以使用接口中定义的方法了。见行31和行79
step5:不再使用服务时,可以解除绑定。见行71中省略的内容。
package com.android.music;
import java.lang.String;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Binder;
import android.os.Parcel;
public interface IMediaPlaybackService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.android.music.IMediaPlaybackService
{
    private static final java.lang.String DESCRIPTOR = “com.android.music.IMediaPlaybackService”;
    /** Construct the stub at attach it to the interface. */
   public Stub()
  {      this.attachInterface(this, DESCRIPTOR);}
  public static com.android.music.IMediaPlaybackService asInterface(android.os. IBinder obj)
  {
      if ((obj==null)) {          return null;       }
       android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);
       if (((iin!=null)&&(iin instanceof com.android.music.IMediaPlaybackService)))
       {         return ((com.android.music.IMediaPlaybackService)iin);        }
      return new com.android.music.IMediaPlaybackService.Stub.Proxy(obj);
  }
 public android.os.IBinder asBinder()
  {    return this;  }
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{   
    switch (code)
{
     case INTERFACE_TRANSACTION:
{
     reply.writeString(DESCRIPTOR);
     return true;
}
case TRANSACTION_openfile:
  {
     data.enforceInterface(DESCRIPTOR);
      java.lang.String _arg0;
      _arg0 = data.readString();
      this.openfile(_arg0);
      reply.writeNoException();
      return true;
  }
case TRANSACTION_openfileAsync:
{data.enforceInterface(DESCRIPTOR);
case TRANSACTION_open:
{

本文链接地址: http://blog.redwolf-soft.com/?p=872

原创文章,版权©红狼博客所有, 转载随意,但请注明出处。

    分享到:

相关文章:

  • 无相关文章
分类: Android, Other 标签: ,
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.
订阅评论
  欢迎参与讨论,请在这里发表您的看法、交流您的观点。