Android每个应用程序都运行在自己的进程空间,并且可以从应用程序UI运行另一个服务进程,而且经常会在不同的进程间传递对象。在Android平台,一个进程通常不能访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的基本单元,并且有序的通过进程边界。
通过代码来实现这个数据传输过程是冗长乏味的,Android提供了AIDL工具来处理这项工作。AIDL (Android Interface Definition Language) 是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。
本文以phone book service为例,即可在其他应用中根据用户ID获取用户的信息。
1 Service
首先,我们在Src目录下创建IPhoneBookService
接口(IPhoneBookService.
aidl),该接口为aidl文件,根据传入的ID获取用户信息.在该文件中需要导入import com.synvata.phone.User;
package com.synvata.phone; import com.synvata.phone.User; interface IPhoneBookService{ User lookUpPhone(long id); }
当我们保存文件时,Android ADT工具会自动在gen目录下生成IPhoneBookService.java文件,在文件中我们看到自动生成了Stub 类
public static abstract class Stub extends android.os.Binder implements com.synvata.phone.IPhoneBookService
该类继承于Binder,并且实现了IPhoneBookService,也就是说RemoteService类和普通的Service类没什么不同,只是返回的IBinder对象比较特别,是实现了AIDL接口的Binder。
同样,类似于本地服务,我们创建远程服务并且实现OnBind方法,PhoneBookService,并且定义一个内部类,用于向Client返回stup实例
package com.synvata.phone; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; public class PhoneBookService extends Service { @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return new PhoneServiceImpl(); } public class PhoneServiceImpl extends IPhoneBookService.Stub{ @Override public User lookUpPhone(long id) throws RemoteException { User usr=new User(); usr.setId(101L); usr.setAge(30); usr.setPhone("123 456 7890"); usr.setRegistered(true); // Anonymous user User anonym = new User(); anonym.setPhone("Not Published"); anonym.setRegistered(false); return ( id == usr.getId() ? usr : anonym ); } } }
由于我们需要向Client返回一个实例,因此采用Parcel方法使User实现Parcelable接口,同Using the Android Parcel一文中介绍,代码相同,使得User可以序列化并通过进程边界有序传输。
同时我们还要在Src中创建User.aidl,这个文件中有两行代码:
package com.synvata.phone; parcelable User;
为什么要创建这个文件?AIDL contract方法要求,在Service中使用的每一个实现了Parcelable方法类都要创建单独的.aidl文件
最后,我们在AndroidManifest,xml中配置Service
<service
android:name=".PhoneBookService" android:process=":remote">
<intent-filter>
<action android:name="com.synvata.phone.IPhoneBookService"></action>
</intent-filter>
</service>
android:process=":remote",代表在应用程序里,当需要该service时,会自动创建新的进程。而如果是android:process="remote",没有“:”分号的,则创建全局进程,不同的应用程序共享该进程。
至此,Service端创建完成。
2 Client
客户端与local service基本相同,只是需要把Service中gen下的类包导入到Src中,但是gen包下没有User类,因此还需要把Service中的User.java导入,否则无法创建User实例。前文介绍过,绑定这个Service需要三个参数bindService(intent,conn,Service.BIND_AUTO_CREATE);第一个是 Intent;第二个是ServiceConnection对象,我们创建该对象实现其onServiceConnected()和 onServiceDisconnected()来判断连接成功或断开连接,第三个参数是如何创建Service,一般指定绑定时自动创建。
IPhoneBookService phoneBookService; ServiceConnection connection=new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { // TODO Auto-generated method stub phoneBookService=null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { // TODO Auto-generated method stub phoneBookService=IPhoneBookService.Stub.asInterface(service); } };
绑定之后,我们就能通过phoneBookService来访问远程Service的方法,就像使用本地方法一样
Intent intent=new Intent("com.synvata.phone.IPhoneBookService"); bindService(intent, connection, Context.BIND_AUTO_CREATE); User usrUser=phoneBookService.lookUpPhone(101L);