AIDL是什么

AIDL英文全称Android Interface Definition Language,中文Android接口定义语言,在Android中,AIDL定义了程序访问接口,并将对象进行序列化,通过该接口,使得进程间采用IPC(进程间通信机制,比如binder)进行交互、传输数据。

AIDL应用场合

a. 在不同应用程序之间,如果客户端需要访问服务端,并且想要处理多线程任务时,采用AIDL;
b. 如果不需要进行跨进程通信,就可以不使用AIDL,比如绑定本地服务,只需继承binder,在
onBind中返回一个binder对象即可;
c. 如果要跨进程通信,但又不需要多线程操作,就使用Messenger方式,实际上Messenger也是基于AIDL的,
只是把AIDL作为底层使用了。Messenger方式中所有的消息存放在一个队列中,每次只允许一个消息待处理。
如果想让服务能够处理多个请求,就使用AIDL。

AIDL和bindservice(又称为bound services)之间的关系

bindservice一般分为两种情况:绑定本地服务;绑定远程服务。
本地服务就是同一个进程中即同一个应用程序中,绑定远程服务指不同进程中,即不同应用程序中。
AIDL一般用在绑定远程服务中,因为这涉及到跨进程操作,需要通过AIDL定义统一接口,传输序列化数据。
注意:大部分应用程序不应该用AIDL来创建bindservice服务,因为多线程处理,让代码变得复杂,不易维护,特殊情况下才使用AIDL。(参考Android官方文档)

创建AIDL服务分为三步:
创建.aidl文件
实现aidl接口
客户端获取接口
为了更加清晰地描述这个过程,本文给出一个实例辅助分析。

案例

定义一个aidl接口displayInformation,用来显示个人的姓名和年龄信息
先看建立好的目录结构如图1所示

图1 案例app源码结构图

左边是服务端,右边是客户端,对比红色方框内的内容,aidl包中的文件结构都是一样的。

案例源码下载地址:http://yunpan.cn/cLippY2ib7RHH  访问密码 9ebb
a. 创建接口(创建aidl文件)

创建服务端IPersonInformation.aidl文件,包含一个displayInformation方法,显示个人的姓名和年龄

1 2 3 4 5 6 7 package com . example . person . aidl ; import  com . example . person . aidl . Person ; interface IPersonInformation {      String displayInformation ( in Person  requester ) ; }

接口前面不能加访问权限修饰符public、private等。因为displayInformation传递的参数是对象,属于非基本数据类型,需要加上in、out、inout修饰符,其含义是:

如果在客户端输入本地数据,服务端接受数据,就用in修饰符;

如果在客户端接收数据,服务端把本地修改后的数据向客户端传输,就用out修饰符;

如果同时满足in和out,就用inout修饰。

本文中定义的接口displayInformation从客户端接收数据,在服务端组合后返回给客户端,客户端相当于输入,服务端是输出,用in修饰符,即:

1 String displayInformation ( in Person  requester ) ;

除了原始基本数据类型(int, long, char, boolean等)、String 、CharSequence以及包含基本数据类型的list和map外,都需要用import语句把对象引用过来,因为person是对象类型,因此用import引入。

一般情况下,Android中远程接口类型名称都用大写的I开头加上接口的名字,这并不是硬性要求,但为了维护程序风格,这样写可读性较高。因为aidl接口都实现IInterface,字母前缀I的含义是IInterface类,表示这是一个可以提供远程服务的类。

b. 实现接口

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 package com . example . person . service ; // This file is StockQuoteService2.java import  com . example . person . R ; import  android . app . Notification ; import  android . app . NotificationManager ; import  android . app . PendingIntent ; import  android . app . Service ; import  android . content . Intent ; import  android . os . IBinder ; import  android . os . RemoteException ; import  com . example . person . activity . TestActivity ; import  com . example . person . aidl . Person ; import  com . example . person . aidl . IPersonInformation ; public class PersonInformationService extends Service { private NotificationManager  notificationMgr ; public class IPersonInformationImpl  extends IPersonInformation . Stub { public String displayInformation ( Person  person ) throws RemoteException { return "Hello " + person . getName ( ) + "! Your age is: " + person . getAge ( ) ; } } @ Override public void onCreate ( ) { super . onCreate ( ) ; notificationMgr = ( NotificationManager ) getSystemService ( NOTIFICATION_SERVICE ) ; displayNotificationMessage ( "onCreate() called in StockQuoteService2" ) ; } @ Override public void onDestroy ( ) { displayNotificationMessage ( "onDestroy() called in StockQuoteService2" ) ; // Clear all notifications from this service notificationMgr . cancelAll ( ) ; super . onDestroy ( ) ; } public int onStartCommand ( Intent  intent , int flags , int startId ) { return super . onStartCommand ( intent , flags , startId ) ; } @ Override public IBinder  onBind ( Intent  intent ) { displayNotificationMessage ( "onBind() called in StockQuoteService2" ) ; return new IPersonInformationImpl ( ) ; } private void displayNotificationMessage ( String message ) { Notification  notification = new Notification ( R . drawable . emo_im_happy , message , System . currentTimeMillis ( ) ) ; PendingIntent  contentIntent = PendingIntent . getActivity ( this , 0 , new Intent ( this , TestActivity . class ) , 0 ) ; notification . setLatestEventInfo ( this , "StockQuoteService2" , message , contentIntent ) ; notification . flags = Notification . FLAG_NO_CLEAR ; notificationMgr . notify ( R . id . app_notification_id , notification ) ; } }

主要实现核心代如下:

1 2 3 4 5 6 7 public class IPersonInformationImpl  extends IPersonInformation . Stub { public String displayInformation ( Person  person ) throws RemoteException { return "Hello " + person . getName ( ) + "! Your age is: " + person . getAge ( ) ; } }

实现类名为IPersonInformationImpl,继承了IPersonInformation.Stub,IPersonInformation.Stub是一个binder接口,如果在eclipse中建立好aidl文件后,编译时会自动生成与aidl同名的java文件IPersonInformation.java,displayInformation为实现的方法。注意,该接口需要Person类,因此还必须把perons类接口文件import进来才能自动生成IPersonInformation.java,下文会提到Person类。

因为传输的对象为Person,非基本数据类型,需要对其序列化,Android中用Parcelable接口来实现,之所以用Parcelable接口,是因为更快、性能更好,Android中进程间通信中大量使用Parcelable接口。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package com . example . person . aidl ; import  android . os . Parcel ; import  android . os . Parcelable ; public class Person implements Parcelable { private int age ; private String name ; public static final Parcelable . Creator < Person > CREATOR = new Parcelable . Creator < Person > ( ) {         public Person  createFromParcel ( Parcel  in ) {             return new Person ( in ) ;         }         public Person [ ] newArray ( int size ) {             return new Person [ size ] ;         }     } ;     public Person ( ) {     } private Person ( Parcel  in ) { readFromParcel ( in ) ; } @ Override public int describeContents ( ) { // TODO Auto-generated method stub return 0 ; } @ Override public void writeToParcel ( Parcel  out , int flags ) { // TODO Auto-generated method stub         out . writeInt ( age ) ;         out . writeString ( name ) ; } public void readFromParcel ( Parcel  in ) {         age = in . readInt ( ) ;         name = in . readString ( ) ; }     public int getAge ( ) {         return age ;     }     public void setAge ( int age ) {         this . age = age ;     }     public String getName ( ) {         return name ;     }     public void setName ( String name ) {         this . name = name ;     } }

Parcelable接口必须实现三个方法:

Parcelable.Creator // 用来产生Person对象

describeContents // 返回特殊对象类型,一般返回0

writeToParcel // 把数据写入到Parcel对象中,Parcel中文含义为包裹,形象地表明先把数据包装好,等待传输,在接收方接收后再解压包裹从中得到数据,起到安全性作用。

IPersonInformation.aidl在编译时需要用到待序列化对象Person,因此还需要创建Person.aidl文件,Person类没有具体方法,代码相对简单:

1 2 package com . example . person . aidl ; parcelable  Person ;

Person类前加parcelable修饰符,表示这是一个待序列化对象。

在eclipse中编译会自动生成IPersonInformation.java,位于gen目录下,这样服务端文件创建成功。

c. 获取接口

客户端要获取服务端接口,先把aidl目录整体拷贝到客户端,如图2所示:

图2 aidl目录结构

包名com.example.person.aidl也要一致,这个报名和AndroidManifest的包名不是一回事,前者就是文件目录,后者是APP的包名。

创建PersonClientActivity,继承于Activity:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 packagecom.example.personclient.activity;     import  com.example.person.aidl.Person; import  com.example.personclient.R; import  com.example.person.aidl.IPersonInformation;     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;     publicclassPersonClientActivityextendsActivity{     protectedstaticfinalStringTAG= "StockQuoteClient2" ; privateIPersonInformation iPersonInformation= null ;     privateButton bindBtn; privateButton callBtn; privateButton unbindBtn;     privateServiceConnection serviceConnection=newServiceConnection(){     @Override publicvoidonServiceConnected(ComponentName name,IBinder service){ Log.v(TAG, "---------------service: " +service.getClass().toString()); iPersonInformation=IPersonInformation.Stub.asInterface(service); Log.v(TAG, "---------------stockService: " +iPersonInformation.getClass().toString()); callService(); }     @Override publicvoidonServiceDisconnected(ComponentName name){ iPersonInformation= null ; } };     /** Called when the activity is first created. */ @Override publicvoidonCreate(Bundle savedInstanceState){ super .onCreate(savedInstanceState); setContentView(R.layout.main);     bindBtn=(Button)findViewById(R.id.bindBtn); bindBtn.setOnClickListener(buttonClickListener);     callBtn=(Button)findViewById(R.id.callBtn); callBtn.setOnClickListener(buttonClickListener); callBtn.setEnabled( false );     unbindBtn=(Button)findViewById(R.id.unbindBtn); unbindBtn.setOnClickListener(buttonClickListener); unbindBtn.setEnabled( false );     }     OnClickListener buttonClickListener=newOnClickListener(){     @Override publicvoidonClick(Viewv){ // TODO Auto-generated method stub if (v.getId()==R.id.bindBtn){ bindService(newIntent( "com.example.person.PersonInformationService" ),serviceConnection, Context.BIND_AUTO_CREATE); bindBtn.setEnabled( false ); callBtn.setEnabled( true ); unbindBtn.setEnabled( true ); }elseif(v.getId()==R.id.callBtn){ callService(); }elseif(v.getId()==R.id.unbindBtn){ unbindService(serviceConnection); bindBtn.setEnabled( true ); callBtn.setEnabled( false ); unbindBtn.setEnabled( false ); } }     };     privatevoidcallService(){ try { Person person=newPerson(); person.setAge( 32 ); person.setName( "zhulf" ); Stringresponse=iPersonInformation.displayInformation(person); Toast.makeText(PersonClientActivity. this , "Value get from service is: " +response, Toast.LENGTH_SHORT).show(); } catch (RemoteException ee){ Log.e( "MainActivity" ,ee.getMessage(),ee); } }     }

核心代码为:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private ServiceConnection  serviceConnection = new ServiceConnection ( ) { @ Override public void onServiceConnected ( ComponentName  name , IBinder  service ) { Log . v ( TAG , "---------------service: " + service . getClass ( ) . toString ( ) ) ; iPersonInformation = IPersonInformation . Stub . asInterface ( service ) ; Log . v ( TAG , "---------------stockService: " + iPersonInformation . getClass ( ) . toString ( ) ) ; callService ( ) ; } @ Override public void onServiceDisconnected ( ComponentName  name ) { iPersonInformation = null ; } } ;

1 2 bindService ( new Intent ( "com.example.person.PersonInformationService" ) , serviceConnection , Context . BIND_AUTO_CREATE ) ;

单击绑定按钮时调用binderservice把serviceConnection与PersonInformationService服务端绑定,当bindservice绑定成功后,系统会调用onServiceConnected方法,第二个参数service是一个IBinder接口对象,由服务端onBind方法返回给客户端的,IPersonInformation.Stub的asInterface返回一个客户端代理对象proxy,赋值给IPersonInformation对象,这样,客户端就获得了服务端接口。

d. 使用服务

既然客户端已经获得了服务端的接口对象,那么就可以调用服务端的方法来得到服务。

1 2 3 4 Person  person = new Person ( ) ; person . setAge ( 32 ) ; person . setName ( "zhulf" ) ; String response = iPersonInformation . displayInformation ( person ) ;

先设置person的内容,而后采用服务端对象iPersonInformation调用服务端的方法displayInformation来获得所需要的数据,这可以形象地描述成客户端得到了服务端的服务!

把生成的服务端、客户端apk安装后,打开客户端,如图3所示,点击bind按钮后显示如图4,客户端设置了姓名”jack”,年龄”100″已经通过远程服务接口displayInformation显示出来了。

图3 bind按钮点击前

图4 bind按钮点击后

点击CALL AGAIN按钮后重复显示toast,点击UNBIND按钮后停止服务。

AIDL服务的详细执行过程

a. PersonInformationService启动过程

bindService通过Intent的action——”com.example.person.PersonInformationService”启动PersonInformationService服务,服务启动成功后由onBind返回一个IPersonInformationImpl对象,IPersonInformationImpl继承了IPersonInformation.Stub,IPersonInformation.Stub又继承了Binder,Binder实现了IBinder接口,因此,onBind返回的是一个IBinder接口对象,通过添加Logcat打印发现实际返回的是一个BinderProxy对象,这是什么原因?

这是binder进程间通信机制所决定的,服务启动后,系统会生成一个binder代理对象binderProxy,用作桥梁,与C++层的BpBinder对应,在aidl服务中通过binderProxy生成prox客户端代理对象。

b. 客户端获取代理接口

上文提到binderProxy用来生成proxy对象,如何生成?上文提到aidl文件在编译时会自动生成java文件,即IPersonInformation.java,源码:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 /*   * This file is auto-generated.  DO NOT MODIFY.   * Original file: E:\\demos_from_internet\\PersonInformationClient\\PersonInformationClient\\src\\com\\example\\person\\aidl\\IPersonInformation.aidl   */ packagecom.example.person.aidl;     publicinterfaceIPersonInformation extendsandroid.os.IInterface{ /** Local-side IPC implementation stub class. */ publicstaticabstractclassStub extendsandroid.os.Binder implementscom.example.person.aidl.IPersonInformation{ privatestaticfinaljava.lang.StringDESCRIPTOR="com.example.person.aidl.IPersonInformation";     /** Construct the stub at attach it to the interface. */ publicStub(){ this.attachInterface(this,DESCRIPTOR); }     /**   * Cast an IBinder object into an   * com.example.person.aidl.IPersonInformation interface, generating a   * proxy if needed.   */ publicstaticcom.example.person.aidl.IPersonInformation asInterface(android.os.IBinder obj){ if ((obj== null )){ returnnull; } android.os.IInterface iin=obj.queryLocalInterface(DESCRIPTOR); if (((iin!= null )&&(iin instanceofcom.example.person.aidl.IPersonInformation))){ return ((com.example.person.aidl.IPersonInformation)iin); } returnnewcom.example.person.aidl.IPersonInformation.Stub.Proxy(obj); }     @Override publicandroid.os.IBinder asBinder(){ returnthis; }     @Override publicbooleanonTransact(intcode,android.os.Parcel data,android.os.Parcel reply,intflags) throws  android.os.RemoteException{ switch (code){ caseINTERFACE_TRANSACTION:{ reply.writeString(DESCRIPTOR); returntrue; } caseTRANSACTION_displayInformation:{ data.enforceInterface(DESCRIPTOR); com.example.person.aidl.Person _arg0; if (( 0 !=data.readInt())){ _arg0=com.example.person.aidl.Person.CREATOR.createFromParcel(data); } else { _arg0= null ; } java.lang.String_result= this .displayInformation(_arg0); reply.writeNoException(); reply.writeString(_result); returntrue; } } returnsuper.onTransact(code,data,reply,flags); }     privatestaticclassProxy implementscom.example.person.aidl.IPersonInformation{ privateandroid.os.IBinder mRemote;     Proxy(android.os.IBinder remote){ mRemote=remote; }     @Override publicandroid.os.IBinder asBinder(){ returnmRemote; }     publicjava.lang.StringgetInterfaceDescriptor(){ returnDESCRIPTOR; }     @Override publicjava.lang.StringdisplayInformation(com.example.person.aidl.Person requester) throws  android.os.RemoteException{ android.os.Parcel _data=android.os.Parcel.obtain(); android.os.Parcel _reply=android.os.Parcel.obtain(); java.lang.String_result; try { _data.writeInterfaceToken(DESCRIPTOR); if ((requester!= null )){ _data.writeInt( 1 ); requester.writeToParcel(_data, 0 ); } else { _data.writeInt( 0 );

更多相关文章

  1. Android(安卓)aidl Binder框架浅析
  2. Android(安卓)Service、Messenger进程间通信
  3. 基于iMAG开发的ITeye手机客户端
  4. Android(安卓)实现联网(四)——TCP/UDP
  5. :Android和j2me的初级对比
  6. Android的MediaPlayer
  7. Android(安卓)Asynchronous Http Client-Android异步网络请求客
  8. Android(安卓)Messenger 进程间通信
  9. Android(安卓)客户端与服务器端进行数据交互(二、登录客户端)

随机推荐

  1. 探索Android(安卓)FrameWork底层开发
  2. Android(安卓)apk文件拆解与重新打包
  3. Ubuntu下Eclipse3.6.1安装ADT插件可能遇
  4. AIDL源码解析in、out和inout
  5. 关于获取手机通讯录联系人Contacts被depr
  6. Android::开机自启动C程序
  7. Android(安卓)7.0后访问文件权限:android.
  8. Android--Activity运用实验
  9. Android(安卓)Binder框架实现之Parcel详
  10. android学习日记(一) 获取某一会话的所有