您的位置:首页 > 移动开发 > Android开发

Android AIDL使用详解 +整合应用

2013-08-06 09:01 337 查看
AIDL (Android interface Definition Language) 

    
AIDL:Android Interface Definition Language,即Android接口定义语言。

Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。
为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition
Language,IDL)来公开服务的接口。我们知道4个Android应用程序组件中的3个(Activity、BroadcastReceiver和ContentProvider)都可以进行跨进程访问,另外一个Android应用程序组件Service同样可以。因此,可以将这种可以跨进程访问的服务称为AIDL(Android Interface Definition Language)服务。

建立AIDL服务的步骤

建立AIDL服务要比建立普通的服务复杂一些,具体步骤如下:
(1)在Eclipse Android工程的Java包目录中建立一个扩展名为aidl的文件。该文件的语法类似于Java代码,但会稍有不同。
(2)如果aidl文件的内容是正确的,ADT会自动生成一个Java接口文件(*.java)。
(3)建立一个服务类(Service的子类)。
(4)实现由aidl文件生成的Java接口。
(5)在AndroidManifest.xml文件中配置AIDL服务,尤其要注意的是,<action>标签中android:name的属性值就是客户端要引用该服务的ID,也就是Intent类的参数值。

1.什么是aidl:aidl是 Android Interface definition language的缩写,一看就明白,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口

icp:interprocess communication :内部进程通信

 

2.既然aidl可以定义并实现进程通信,那么我们怎么使用它呢?文档/android-sdk/docs/guide/developing/tools/aidl.html中对步骤作了详细描述:

--1.Create your .aidl file - This file defines an interface (YourInterface.aidl) that defines the methods and fields available to a client.

创建你的aidl文件,我在后面给出了一个例子,它的aidl文件定义如下:写法跟java代码类似,但是这里有一点值得注意的就是它可以引用其它aidl文件中定义的接口,但是不能够引用你的java类文件中定义的接口

[java]
view plaincopy

package com.cao.android.demos.binder.aidl;    
import com.cao.android.demos.binder.aidl.AIDLActivity;  
interface AIDLService {     
    void registerTestCall(AIDLActivity cb);     
    void invokCallBack();  
}    

--2.Add the .aidl file to your makefile - (the ADT Plugin for Eclipse manages this for you). Android includes the compiler, called AIDL, in the tools/ directory.

编译你的aidl文件,这个只要是在eclipse中开发,你的adt插件会像资源文件一样把aidl文件编译成java代码生成在gen文件夹下,不用手动去编译:编译生成AIDLService.java如我例子中代码



--3.Implement your interface methods - The AIDL compiler creates an interface in the Java programming language from your AIDL interface. This interface has an inner abstract class named Stub that inherits the interface (and implements a few additional methods
necessary for the IPC call). You must create a class that extends YourInterface.Stub and implements the methods you declared in your .aidl file.

实现你定义aidl接口中的内部抽象类Stub,public static abstract class Stub extends android.os.Binder implements com.cao.android.demos.binder.aidl.AIDLService

Stub类继承了Binder,并继承我们在aidl文件中定义的接口,我们需要实现接口方法,下面是我在例子中实现的Stub类:

 

[java]
view plaincopy

private final AIDLService.Stub mBinder = new AIDLService.Stub() {  
  
    @Override  
    public void invokCallBack() throws RemoteException {  
        Log("AIDLService.invokCallBack");  
        Rect1 rect = new Rect1();  
        rect.bottom=-1;  
        rect.left=-1;  
        rect.right=1;  
        rect.top=1;  
        callback.performAction(rect);  
    }  
  
  
    @Override  
    public void registerTestCall(AIDLActivity cb) throws RemoteException {  
        Log("AIDLService.registerTestCall");  
        callback = cb;  
    }  
};  

Stub翻译成中文是存根的意思,注意Stub对象是在被调用端进程,也就是服务端进程,至此,服务端aidl服务端得编码完成了。

--4.Expose your interface to clients - If you're writing a service, you should extend Service and override Service.onBind(Intent) to return an instance of your class that implements your interface.

第四步告诉你怎么在客户端如何调用服务端得aidl描述的接口对象,doc只告诉我们需要实现Service.onBind(Intent)方法,该方法会返回一个IBinder对象到客户端,绑定服务时不是需要一个ServiceConnection对象么,在没有了解aidl用法前一直不知道它是什么作用,其实他就是用来在客户端绑定service时接收service返回的IBinder对象的:

[java]
view plaincopy

AIDLService mService;  
private ServiceConnection mConnection = new ServiceConnection() {  
    public void onServiceConnected(ComponentName className, IBinder service) {  
        Log("connect service");  
        mService = AIDLService.Stub.asInterface(service);  
        try {  
            mService.registerTestCall(mCallback);  
        } catch (RemoteException e) {  
  
        }  
    }  
  
  
    public void onServiceDisconnected(ComponentName className) {  
        Log("disconnect service");  
        mService = null;  
    }  
};  

 

mService就是AIDLService对象,具体可以看我后面提供的示例代码,需要注意在客户端需要存一个服务端实现了的aidl接口描述文件,但是客户端只是使用该aidl接口,不需要实现它的Stub类,获取服务端得aidl对象后mService = AIDLService.Stub.asInterface(service);,就可以在客户端使用它了,对mService对象方法的调用不是在客户端执行,而是在服务端执行。

4.aidl中使用java类,需要实现Parcelable接口,并且在定义类相同包下面对类进行声明:

上面我定义了Rect1类

之后你就可以在aidl接口中对该类进行使用了

package com.cao.android.demos.binder.aidl;
import com.cao.android.demos.binder.aidl.Rect1;
interface AIDLActivity {
void performAction(in Rect1 rect);
}


 

注意in/out的说明,我这里使用了in表示输入参数,out没有试过,为什么使用in/out暂时没有做深入研究。

5.aidl使用完整示例,为了清除说明aidl使用,我这里写了一个例子,例子参考了博客:

http://blog.csdn.net/saintswordsman/archive/2010/01/04/5130947.aspx

作出说明

    例子实现了一个AIDLTestActivity,AIDLTestActivity通过bindservice绑定一个服务AIDLTestService,通过并获取AIDLTestActivity的一个aidl对象AIDLService,该对象提供两个方法,一个是registerTestCall注册一个aidl对象,通过该方法,AIDLTestActivity把本身实现的一个aidl对象AIDLActivity传到AIDLTestService,在AIDLTestService通过操作AIDLActivity这个aidl远端对象代理,使AIDLTestActivity弹出一个toast,完整例子见我上传的资源:

http://download.csdn.net/source/3284820

文章仓促而成,有什么疑问欢迎大家一起讨论。

原帖子链接:

http://blog.csdn.net/stonecao/article/details/6425019

欢迎阅读本文,你能关注本文,你知道你需要进程间通信、需要AIDL(以及Binder),那么可以默认你对这些概念已经有了一些了解,你(大致)知道它们是什么,它们有什么用,所以为了节约大家的眼力和时间,在此我不复制粘贴网上泛滥的博客或者翻译冗长的android文档。

      关于AIDL的介绍在文档:docs/guide/developing/tools/aidl.html

      关于IBinder的介绍在文档:docs/reference/android/os/IBinder.html

      以及Binder:docs/reference/android/os/Binder.html

      在后文中,我将以我自己的理解向你介绍相关的概念。以我目前粗浅的经验,应用程序使用AIDL的地方,几乎都和Service有关,所以你也需要知道一些关于Service的知识。日后得闲我也会继续写一些关于Service的贴。

      本文将以一个例子来和你分享使用AIDL的基础技能,这个例子里有:

1、一个类mAIDLActivity,继承Activity。里面有三个按钮,text分别为StartService,StopService,CallbackTest。

2、一个类mAIDLService,继承Service。为了充分展示ADIL的功能,它做以下工作:当用户点击CallbackTest按钮时,从mAIDLActivity调用mAIDLService中的Stub对象的一个方法invokCallBack(),而这个方法又会调用mAIDLActivity中Stub对象的一个方法performAction(),这个方法在屏幕上显示一个toast。没什么意义,只是展示一下AIDL如何使用。

3、两个AIDL文件:forService.aidl和forActivity.aidl。对应名字,在Service和Activity中分别有对象需要用到它们定义的接口。

4、相关XML文件,略过。关于manifest中Service的语法,见docs/guide/topics/manifest/service-element.html。你也可以简单地在<application></application>中加入

     <service android:name=".mAIDLService" android:process=":remote"> </service>

开发环境为Eclipse。

拣重要的先说,来看看aidl文件的内容:

文件:forActivity.aidl

[java]
view plaincopy

package com.styleflying.AIDL;  
interface forActivity {  
    void performAction();  
    }  

 
文件:forService.aidl

[java]
view plaincopy

package com.styleflying.AIDL;  
import com.styleflying.AIDL.forActivity;  
interface forService {  
    void registerTestCall(forActivity cb);  
    void invokCallBack();  
}  

 
这两个文件和Java文件放置的地方一样,看包名。

在Eclipse中它们将被自动编译为forActivity.java和forService.java,它们存放在gen目录下。为了方便手头无法演练的读者,代码贴上,不用细看。

文件forActivity.java:

[java]
view plaincopy

/* 
 * This file is auto-generated.  DO NOT MODIFY. 
 * Original file: D://workspace//AIDLTest//src//com//styleflying//AIDL//forActivity.aidl 
 */  
package com.styleflying.AIDL;  
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 forActivity extends android.os.IInterface  
{  
/** Local-side IPC implementation stub class. */  
public static abstract class Stub extends android.os.Binder implements com.styleflying.AIDL.forActivity  
{  
private static final java.lang.String DESCRIPTOR = "com.styleflying.AIDL.forActivity";  
/** Construct the stub at attach it to the interface. */  
public Stub()  
{  
this.attachInterface(this, DESCRIPTOR);  
}  
/** 
 * Cast an IBinder object into an forActivity interface, 
 * generating a proxy if needed. 
 */  
public static com.styleflying.AIDL.forActivity 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.styleflying.AIDL.forActivity))) {  
return ((com.styleflying.AIDL.forActivity)iin);  
}  
return new com.styleflying.AIDL.forActivity.Stub.Proxy(obj);  
}  
public android.os.IBinder asBinder()  
{  
return this;  
}  
@Override 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_performAction:  
{  
data.enforceInterface(DESCRIPTOR);  
this.performAction();  
reply.writeNoException();  
return true;  
}  
}  
return super.onTransact(code, data, reply, flags);  
}  
private static class Proxy implements com.styleflying.AIDL.forActivity  
{  
private android.os.IBinder mRemote;  
Proxy(android.os.IBinder remote)  
{  
mRemote = remote;  
}  
public android.os.IBinder asBinder()  
{  
return mRemote;  
}  
public java.lang.String getInterfaceDescriptor()  
{  
return DESCRIPTOR;  
}  
public void performAction() throws android.os.RemoteException  
{  
android.os.Parcel _data = android.os.Parcel.obtain();  
android.os.Parcel _reply = android.os.Parcel.obtain();  
try {  
_data.writeInterfaceToken(DESCRIPTOR);  
mRemote.transact(Stub.TRANSACTION_performAction, _data, _reply, 0);  
_reply.readException();  
}  
finally {  
_reply.recycle();  
_data.recycle();  
}  
}  
}  
static final int TRANSACTION_performAction = (IBinder.FIRST_CALL_TRANSACTION + 0);  
}  
public void performAction() throws android.os.RemoteException;  
}  

文件forService.java:

[java]
view plaincopy

/* 
 * This file is auto-generated.  DO NOT MODIFY. 
 * Original file: D://workspace//AIDLTest//src//com//styleflying//AIDL//forService.aidl 
 */  
package com.styleflying.AIDL;  
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 forService extends android.os.IInterface  
{  
/** Local-side IPC implementation stub class. */  
public static abstract class Stub extends android.os.Binder implements com.styleflying.AIDL.forService  
{  
private static final java.lang.String DESCRIPTOR = "com.styleflying.AIDL.forService";  
/** Construct the stub at attach it to the interface. */  
public Stub()  
{  
this.attachInterface(this, DESCRIPTOR);  
}  
/** 
 * Cast an IBinder object into an forService interface, 
 * generating a proxy if needed. 
 */  
public static com.styleflying.AIDL.forService 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.styleflying.AIDL.forService))) {  
return ((com.styleflying.AIDL.forService)iin);  
}  
return new com.styleflying.AIDL.forService.Stub.Proxy(obj);  
}  
public android.os.IBinder asBinder()  
{  
return this;  
}  
@Override 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_registerTestCall:  
{  
data.enforceInterface(DESCRIPTOR);  
com.styleflying.AIDL.forActivity _arg0;  
_arg0 = com.styleflying.AIDL.forActivity.Stub.asInterface(data.readStrongBinder());  
this.registerTestCall(_arg0);  
reply.writeNoException();  
return true;  
}  
case TRANSACTION_invokCallBack:  
{  
data.enforceInterface(DESCRIPTOR);  
this.invokCallBack();  
reply.writeNoException();  
return true;  
}  
}  
return super.onTransact(code, data, reply, flags);  
}  
private static class Proxy implements com.styleflying.AIDL.forService  
{  
private android.os.IBinder mRemote;  
Proxy(android.os.IBinder remote)  
{  
mRemote = remote;  
}  
public android.os.IBinder asBinder()  
{  
return mRemote;  
}  
public java.lang.String getInterfaceDescriptor()  
{  
return DESCRIPTOR;  
}  
public void registerTestCall(com.styleflying.AIDL.forActivity cb) throws android.os.RemoteException  
{  
android.os.Parcel _data = android.os.Parcel.obtain();  
android.os.Parcel _reply = android.os.Parcel.obtain();  
try {  
_data.writeInterfaceToken(DESCRIPTOR);  
_data.writeStrongBinder((((cb!=null))?(cb.asBinder()):(null)));  
mRemote.transact(Stub.TRANSACTION_registerTestCall, _data, _reply, 0);  
_reply.readException();  
}  
finally {  
_reply.recycle();  
_data.recycle();  
}  
}  
public void invokCallBack() throws android.os.RemoteException  
{  
android.os.Parcel _data = android.os.Parcel.obtain();  
android.os.Parcel _reply = android.os.Parcel.obtain();  
try {  
_data.writeInterfaceToken(DESCRIPTOR);  
mRemote.transact(Stub.TRANSACTION_invokCallBack, _data, _reply, 0);  
_reply.readException();  
}  
finally {  
_reply.recycle();  
_data.recycle();  
}  
}  
}  
static final int TRANSACTION_registerTestCall = (IBinder.FIRST_CALL_TRANSACTION + 0);  
static final int TRANSACTION_invokCallBack = (IBinder.FIRST_CALL_TRANSACTION + 1);  
}  
public void registerTestCall(com.styleflying.AIDL.forActivity cb) throws android.os.RemoteException;  
public void invokCallBack() throws android.os.RemoteException;  
}  

两段代码差不多,前面基本一样,从后面看,最后跟着我们在AIDL中自定义的方法,没有实现。两个文件各定义一个了接口,这两个接口分别会在Activity和Service中使用,在那里我们将实现自定义的方法。两个接口中都定义了一个抽象类Stub,实现所在的接口。Stub中又有一个类Proxy。Stub中有一个static的asInterface()方法,里面有很多return语句,在mAIDLActivity中调用它时,它返回一个新创建的内部类Proxy对象。

这个Stub对我们来说很有用,它继承了Binder。Binder有什么用呢?一个类,继承了Binder,那么它的对象就可以被远程的进程使用了(前提是远程进程获取了这个类的对象【对象的引用】,至于如如何获得看下文),在本例中就是说,如果一个Service中有一个继承了Stub的类的对象,那么这个对象中的方法就可以在Activity中使用,对Activity也是这样。至于Binder的细节,网上有很多贴介绍,看不明白也不影响我们完成这个例子。

再看mAIDLActivity.java:

[java]
view plaincopy

package com.styleflying.AIDL;  
import android.app.Activity;  
import android.content.ComponentName;  
import android.content.Context;  
import android.content.Intent;  
import android.content.ServiceConnection;  
import android.os.Bundle;  
import android.os.IBinder;  
import android.os.RemoteException;  
import android.util.Log;  
import android.view.View;  
import android.view.View.OnClickListener;  
import android.widget.Button;  
import android.widget.Toast;  
public class mAIDLActivity extends Activity {  
    private static final String TAG = "AIDLActivity";  
    private Button btnOk;  
    private Button btnCancel;  
    private Button btnCallBack;  
      
    private void Log(String str) {  
        Log.d(TAG, "------ " + str + "------");  
        }  
      
    private forActivity mCallback = new forActivity.Stub() {  
        public void performAction() throws RemoteException  
        {  
            Toast.makeText(mAIDLActivity.this, "this toast is called from service", 1).show();  
        }  
        };  
          
    forService mService;  
    private ServiceConnection mConnection = new ServiceConnection() {  
        public void onServiceConnected(ComponentName className,  
                IBinder service) {  
            mService = forService.Stub.asInterface(service);  
            try {  
                mService.registerTestCall(mCallback);}  
            catch (RemoteException e) {  
                  
            }  
            }  
        public void onServiceDisconnected(ComponentName className) {  
            Log("disconnect service");  
            mService = null;  
            }  
        };  
    @Override  
    public void onCreate(Bundle icicle) {  
        super.onCreate(icicle);  
        setContentView(R.layout.main);  
        btnOk = (Button)findViewById(R.id.btn_ok);  
        btnCancel = (Button)findViewById(R.id.btn_cancel);  
        btnCallBack = (Button)findViewById(R.id.btn_callback);  
        btnOk.setOnClickListener(new OnClickListener() {  
            public void onClick(View v) {  
                Bundle args = new Bundle();  
                Intent intent = new Intent(mAIDLActivity.this, mAIDLService.class);  
                intent.putExtras(args);  
                bindService(intent, mConnection, Context.BIND_AUTO_CREATE);  
                startService(intent);  
                }  
            });  
        btnCancel.setOnClickListener(new OnClickListener() {  
            public void onClick(View v) {  
                unbindService(mConnection);  
                //stopService(intent);  
                }  
            });  
        btnCallBack.setOnClickListener(new OnClickListener() {  
              
            @Override  
            public void onClick(View v)  
            {  
                try  
                {  
                    mService.invokCallBack();  
                } catch (RemoteException e)  
                {  
                    // TODO Auto-generated catch block  
                    e.printStackTrace();  
                }  
            }  
        });  
        }  
}  

 

很短,相信大家很容易看明白。注意mConnection,它的onServiceConnected()中有一句mService = forService.Stub.asInterface(service);给mService赋值了,这个mService是一个forService,而service是onServiceConnected()传进来的参数,onServiceConnected()会在连接Service的时候被系统调用,这个service参数的值来自哪里呢?看mAIDLService.java:

[java]
view plaincopy

package com.styleflying.AIDL;  
import android.app.Service;  
import android.content.Intent;  
import android.os.IBinder;  
import android.os.RemoteCallbackList;  
import android.os.RemoteException;  
import android.util.Log;  
public class mAIDLService extends Service {  
    private static final String TAG = "AIDLService";    
    private forActivity callback;  
    private void Log(String str) {  
        Log.d(TAG, "------ " + str + "------");  
    }  
    @Override  
    public void onCreate() {  
        Log("service create");  
    }  
    @Override  
    public void onStart(Intent intent, int startId) {  
        Log("service start id=" + startId);  
    }  
      
    @Override  
    public IBinder onBind(Intent t) {  
        Log("service on bind");  
        return mBinder;  
    }  
    @Override  
    public void onDestroy() {  
        Log("service on destroy");  
        super.onDestroy();  
    }  
    @Override  
    public boolean onUnbind(Intent intent) {  
        Log("service on unbind");  
        return super.onUnbind(intent);  
    }  
    public void onRebind(Intent intent) {  
        Log("service on rebind");  
        super.onRebind(intent);  
    }  
    private final forService.Stub mBinder = new forService.Stub() {  
        @Override  
        public void invokCallBack() throws RemoteException  
        {  
            callback.performAction();  
              
        }  
        @Override  
        public void registerTestCall(forActivity cb) throws RemoteException  
        {  
            callback = cb;  
              
        }  
          
    };  
}  

注意onBind(),它的返回类型为IBinder,返回了一个mBinder,看看mBinder的定义:

    private final forService.Stub mBinder = new forService.Stub() {

        @Override

        public void invokCallBack() throws RemoteException

        {

            callback.performAction();

         }

        @Override

        public void registerTestCall(forActivity cb) throws RemoteException

        {

            callback = cb;

        }

       };

它是实现了我们在AIDL中定义的方法,这个mBinder最终返回给了mAIDLActivity中的mService,于是在mAIDLActivity中可以使用mBinder中的方法了。在mAIDLActivity中也有一个类似mBinder的对象,看看定义:   

        private forActivity mCallback = new forActivity.Stub()

    {

        public void performAction() throws RemoteException

        {

            Toast.makeText(mAIDLActivity.this, "this toast is called from service", 1).show();

        }

      };

我们要在界面上显示一个toast,就是在这里实现的。这个对象,在mConnection的onServiceConnected()被调用时,通过调用mService(也就是远程的mAIDLService中的mBinder)的registerTestCall(),传递给了mAIDLService,于是在mAIDLService中可以调用performAction()了。

很啰嗦,只为了能把这个细节说清楚。请大家认真看,我尽量避免错别字、混乱的大小写和逻辑不清的语法,相信你会看明白。是不是很简单?再啰嗦一下,做一个大致总结,我们使用AIDL是要做什么呢:

让Acticity(或者说一个进程/一个类?)和Service(或者说远端进程/远端类/对象?)获取对方的一个Stub对象,这个对象在定义时实现了我们在AIDL中定义的方法,于是这些远程对象中的方法可以在本地使用了。如果这种使用(通信)是单向的,比如只是Activity需要通知Service做什么,那么只要Service中有一个Stub对象,并且传给Acticity就够了。

至于如何获得远程的Stub,参看上面的代码,看mConnection、registerTestCall、onRebind,它们展示了一种方法。

另外,有时候我们可能在一个类中有多个Stub对象,它们都要给远程交互的类的实例,这个时候可以考虑使用RemoteCallbackList<>(docs/reference/android/os/RemoteCallbackList.html)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  aidl interface