跨进程访问(AIDL服务)

Android系统中的进程之间不能共 享内存,因此,需要提供一些机制在不同进程之间进行数据通信。在4个Android应用程序组件中的3个(Activity、Broadcast和 Content Provider)都可以进行跨进程访问,另外一个Android应用程序组件Service同样可以,也即AIDL服务。


什么是AIDL服务
为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。因此,可以将这种可以跨进程访问的服务称为AIDL(Android Interface Definition Language)服务。
AIDL (Android Interface Definition Language)是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中 (例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。

AIDL IPC机制是面向接口的,像COM或Corba一样,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。

使用AIDL实现IPC(Implementing IPC Using AIDL):

1. 创建.aidl文件-该文件(YourInterface.aidl)定义了客户端可用的方法和数据的接口。

2. 在makefile文件中加入.aidl文件-(Eclipse中的ADT插件提供管理功能)Android包括名为AIDL的编译器,位于tools/文件夹。

3. 实现接口-AIDL编译器从AIDL接口文件中利用Java语言创建接口,该接口有一个继承的命名为Stub的内部抽象类(并且实现了一些IPC调用的附加方法),要做的就是创建一个继承于YourInterface.Stub的类并且实现在.aidl文件中声明的方法。

4. 向客户端公开接口-如果是编写服务,应该继承Service并且重载Service.onBind(Intent) 以返回实现了接口的对象实例


在创建YourInterface.aidl文件时,需要注意: AIDL 服务只支持有限的数据类型,即Java基本类型、集合类型、AIDL 自动生成的接口(需要手动import),如果需要使用复杂的数据就需要做更一步处理,比如实现了android.os.Parcelable 接口的类(需要import),而且要注意为实现了Parcelable 接口的类创建一个对应的aidl文件,文件名和类名相同,文件内容为:除了package必须有parcelable YourInterface。

一个简单的AIDL设计

创建AIDL文件,在这个aidl中使用的复杂的数据类型(Person,Male,Female都实现了接口android.os.Parcelable):

package com.aidl.service;import com.aidl.service.Person;import com.aidl.service.Male;import com.aidl.service.Female;interface IPersonService {Female getFemale(String name);Male getMale(String name);Person getPerson(String name);List<Person> getPersons();List<Female> getFemales();List<Male> getMales();void createPerson(in Person person);void createFemale(in String name, in String telNumber, in int age);void createMale(in String name, in String telNumber, in int age);int getFemaleCount();int getMaleCount();int getPersonCount();}

Person.aild(Male,Female类似)很简单:

package com.aidl.service;parcelable Person; 

此时可以编译项目,Eclipse会在gen下自动生成IPersonService.java,该类中自动生成了Stub抽象类以及asInterface()等:

package com.aidl.service;public interface IPersonService extends android.os.IInterface{/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements com.aidl.service.IPersonService{private static final java.lang.String DESCRIPTOR = "com.aidl.service.IPersonService";/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR);}/** * Cast an IBinder object into an com.aidl.service.IPersonService interface, * generating a proxy if needed. */public static com.aidl.service.IPersonService 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.aidl.service.IPersonService))) {return ((com.aidl.service.IPersonService)iin);}return new com.aidl.service.IPersonService.Stub.Proxy(obj);}public android.os.IBinder asBinder(){return this;}/** * 省略...... */public void createPerson(com.aidl.service.Person person) throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);if ((person!=null)) {_data.writeInt(1);person.writeToParcel(_data, 0);}else {_data.writeInt(0);}mRemote.transact(Stub.TRANSACTION_createPerson, _data, _reply, 0);_reply.readException();}finally {_reply.recycle();_data.recycle();}}public void createFemale(java.lang.String name, java.lang.String telNumber, int age) 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.writeString(name);_data.writeString(telNumber);_data.writeInt(age);mRemote.transact(Stub.TRANSACTION_createFemale, _data, _reply, 0);_reply.readException();}finally {_reply.recycle();_data.recycle();}}}public com.aidl.service.Female getFemale(java.lang.String name) throws android.os.RemoteException;public com.aidl.service.Male getMale(java.lang.String name) throws android.os.RemoteException;public com.aidl.service.Person getPerson(java.lang.String name) throws android.os.RemoteException;public java.util.List<com.aidl.service.Person> getPersons() throws android.os.RemoteException;public java.util.List<com.aidl.service.Female> getFemales() throws android.os.RemoteException;public java.util.List<com.aidl.service.Male> getMales() throws android.os.RemoteException;public void createPerson(com.aidl.service.Person person) throws android.os.RemoteException;public void createFemale(java.lang.String name, java.lang.String telNumber, int age) throws android.os.RemoteException;public void createMale(java.lang.String name, java.lang.String telNumber, int age) throws android.os.RemoteException;public int getFemaleCount() throws android.os.RemoteException;public int getMaleCount() throws android.os.RemoteException;public int getPersonCount() throws android.os.RemoteException;}

配置Manifest.xml文件,下面是一个最最简单的配置方式:

<service android:name="com.aidl.service.PersonService"><intent-filter><action android:name="com.aidl.service.action.PersonService" /></intent-filter></service>

实现Service,类名可以根据自己喜好来定,Service中最主要的是实现aidl中各个方法,即,Stub中没有实现的抽象方法这里需要注意onBind()返回Stub:

package com.aidl.service;import com.aidl.service.Person.SEX;public class PersonService extends Service {private final String TAG = "PersonService";@Overridepublic void onCreate() {super.onCreate();males = new ArrayList<Male>();females = new ArrayList<Female>();mPerson = new ArrayList<Person>();}@Overridepublic void onDestroy() {super.onDestroy();}@Overridepublic boolean onUnbind(Intent intent) {return super.onUnbind(intent);}@Overridepublic IBinder onBind(Intent intent) {return mPersonBinder;}private List<Male> males;private List<Female> females;private List<Person> mPerson;private IPersonService.Stub mPersonBinder = new IPersonService.Stub() {@Overridepublic Female getFemale(String name) throws RemoteException {Female result = null;if (females == null || name == null || name.length() == 0)return null;for (Female f : females) {if (f.getName() != null && f.getName().length() > 0&& f.getName().equals(name)) {result = f;break;}}return result;}/** *  * 此处省略其余方法的实现 * */}

接下来可以定义自己外部使用的ServiceConnection,主要是onServiceConnected()构造你的接口类型,onServiceDisconnected()断开服务时处理,通常我们的ServiceConnection是一个单例,实现了Service的连接(connect)/断开(unconnect)方法:

package com.aidl.service;public class PersonServiceConnent implements ServiceConnection {private static final String TAG = "PersonServiceConnent";private Context mContext;private static PersonServiceConnent connect;private final static Object synObj = new Object();private IPersonService myInterface;public static final String SERVICE_ACTION = "com.aidl.service.action.PersonService";public static final String SERVICE_PACKAGENAME = "com.aidl.main";public static final String SERVICE_CLASSNAME = "com.aidl.service.PersonService";@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {myInterface = IPersonService.Stub.asInterface(service);if (myInterface == null)Log.i(TAG, "in onServiceConnected() getCurrentPosition == null");elseLog.i(TAG, "in onServiceConnected() getCurrentPosition != null");}@Overridepublic void onServiceDisconnected(ComponentName name) {myInterface = null;bConnected = false;Log.i(TAG, "onServiceDisconnected:" + name);}private PersonServiceConnent(Context context) {mContext = context;}public static PersonServiceConnent Instance(Context context) {if (connect == null) {synchronized (synObj) {if (connect == null)connect = new PersonServiceConnent(context);}}return connect;}private Intent mIntent = new Intent();private boolean bStartService = false;private boolean bConnected = false;private ComponentName mComponentName;public synchronized boolean connect(Context context) {if (context == null)return false;mContext = context;mIntent.setAction(SERVICE_ACTION);if (!bStartService) {mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);try {mComponentName = mContext.startService(mIntent);if (mComponentName != null) {bStartService = true;}} catch (Exception e) {e.printStackTrace();}}if (connect != null && !connect.bConnected) {connect.bConnected = mContext.bindService(mIntent, connect,Context.BIND_AUTO_CREATE);if (!connect.bConnected) {try {mContext.unbindService(connect);} catch (Exception e) {}try {mContext.startActivity(mIntent);} catch (Exception e) {}try {connect.bConnected = mContext.bindService(mIntent, connect,Context.BIND_AUTO_CREATE);} catch (Exception e) {}}}return connect.bConnected;}public void unconnect() {if (mContext == null)return;elseunconnect(mContext);}public void unconnect(Context context) {if (context == null)context = mContext;if (context == null)return;if (connect != null) {if (connect.bConnected) {try {context.unbindService(connect);bConnected = false;} catch (Exception e) {}try {Intent tmpIntent = new Intent();if (mComponentName != null) {tmpIntent.setComponent(mComponentName);} else {tmpIntent.setClassName(SERVICE_PACKAGENAME,SERVICE_CLASSNAME);}context.stopService(tmpIntent);bStartService = false;} catch (Exception e) {Log.e(TAG, "unconnect error", e);}}}}/** *  getFemale()供我们在外部调用ServiceConnection的时候使用,其实现就是调用Service中的方法而已 * @param name * @return */public Female getFemale(String name) {if (myInterface == null) {Log.i(TAG, "in getFemale() myInterface == null");this.connect(mContext);return null;}try {return myInterface.getFemale(name);} catch (RemoteException e) {Log.i(TAG, "getFemale()", e);return null;}}/** *  * 省略其余类似getFemale方法的实现 *  */}

在上面的ServiceConnection的代码中,onServiceConnected获取Service使用了Stub.asInterface(service),这种方式是常见的。其中的connect和unconnect也是比较常见的写法,基本类似。connect中,Intent的Action必须与AndroidManifest.xml中对应<service>的action一致。在unconnect中setClassName(String packageName, String className)的参数需要注意packageName为应用的packageName,而非简单的class的package,className为绝对class(带包名)。

这样就比较完整的实现了一个AIDL,这里的例子虽然比较简单,但是AIDL实现的过程和步骤基本是一样的,而对于ServiceConnection中最主要的几个方法的写法也基本大同小异。

更多相关文章

  1. ANDROID对文件的操作
  2. Android中startService基本使用方法概述
  3. Android判断网络状态方法详解
  4. eclipse:运行 Android 项目时出现 “Unable to execute dex: Mult
  5. android 签名文件获取 MD5、SHA1
  6. HorizontalScrollView不显示滚动条,布局完全填充的方法
  7. Android studio怎么创建一个Java类文件

随机推荐

  1. android画日历
  2. Android获取GPS坐标:
  3. 高德地图Android,绘制自定义定位蓝点、mar
  4. scrollView的fillviewport
  5. Dialog的样式
  6. Android实现圆角照片和圆形照片
  7. android获取NetworkMode
  8. android 音视频录制
  9. Android跳转浏览器打开URL
  10. Android引入外部字体源代码