Android AIDL应用间交互

来源:互联网 发布:高速网络不稳定 编辑:程序博客网 时间:2024/05/01 04:01

[摘要]在Android Service中我们对长时间运行的服务、应用内交互的服务进行了相关介绍,本文主要介绍使用Service进行应用间的交互。

在Android Service中我们对长时间运行的服务、应用内交互的服务进行了相关介绍,本文主要介绍使用Service进行应用间的交互。关于Service的介绍见Android Service介绍。

1、介绍

Android使用AIDL来完成进程间通信(IPC),AIDL全程为Android Interface Definition Language。在服务需要接受不同应用多线程的请求时才需要使用AIDL,如果是同一个应用内的请求使用Binder实现即可,见应用内交互的服务;如果只是应用间通信而不是多线程处理的话使用Messenger,当然这两种情况也可以使用AIDL。本地进程和远程进程使用AIDL有所不同,本地进程内调用时会都在调用的线程内执行,远程进程使用是通过Service进程内一个由系统维护的线程池发出调用,所以可能是未知线程同时调用,需要注意线程安全问题。

2、示例

在被调用应用内(后面称服务端)的Service内有个属性count,需要在调用者(后面称客户端)内对这个属性进行操作。下面将介绍服务器端和客户端的主要代码。

服务器端(代码见AndroidDemo@GoogleCode):

(1) 服务器端新建AIDL文件定义接口

在服务器端的src目录下新建以.aidl为后缀的文件,在这个文件中定义一个接口,声明服务器端和客户端交互的api,语法跟普通java接口声明一样,可以添加中英文注释。不同的是:

a. 除了Java基本数据类型 (int, long, char, boolean等)、String、CharSequence、List、Map外,其他复杂类型都需要显式import(包括其他AIDL定义的接口),即便复杂类型在同一个包内定义。

b. 支持泛型实例化的List,如List<String>;不支持泛型实例化的Map,如Map<String, String>。对于List为参数接收者接收到的始终是ArrayList;对于Map为参数接收者接收到的始终是HashMap。

c. interface和函数都不能带访问权限修饰符。

d. 接口内只允许定义方法,不允许定义静态属性。

我们定义MyAIDLInterface.aidl文件如下:

View Row Code
1package com.trinea.android.demo;23interfaceMyAIDLInterface {4intgetCount();5voidsetCount(int count);6}

声明两个接口在后面供客户端调用。保存文件,编译后,Sdk工具在gen目录下相同子目录中自动生成同名的以.java为后缀的文件,这里文件名为MyAIDLInterface.java。

(2) 自动生成的同名java文件解析

MyAIDLInterface.java为Sdk工具自动生成,内容如下:

View Row Code
1/*2* This file is auto-generated. DO NOT MODIFY.3* Original file: D:\\Eclipse\\AndroidDemo\\MapDemo\\src\\com\\trinea\\android\\demo\\MyAIDLInterface.aidl4*/5package com.trinea.android.demo;67publicinterface MyAIDLInterface extends android.os.IInterface8{9/** Local-side IPC implementation stub class. */10publicstatic abstract class Stub extends android.os.Binderimplements com.trinea.android.demo.MyAIDLInterface11{12privatestatic final java.lang.StringDESCRIPTOR = "com.trinea.android.demo.MyAIDLInterface";13/** Construct the stub at attach it to the interface. */14publicStub()15{16this.attachInterface(this,DESCRIPTOR);17}18/**19* Cast an IBinder object into an com.trinea.android.demo.MyAIDLInterface interface,20* generating a proxy if needed.21*/22publicstatic com.trinea.android.demo.MyAIDLInterfaceasInterface(android.os.IBinder obj)23{24if ((obj==null)) {25returnnull;26}27android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);28if (((iin!=null)&&(iininstanceof com.trinea.android.demo.MyAIDLInterface))) {29return ((com.trinea.android.demo.MyAIDLInterface)iin);30}31returnnew com.trinea.android.demo.MyAIDLInterface.Stub.Proxy(obj);32}33public android.os.IBinderasBinder()34{35returnthis;36}37@Overridepublic boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException38{39switch (code)40{41caseINTERFACE_TRANSACTION:42{43reply.writeString(DESCRIPTOR);44returntrue;45}46caseTRANSACTION_getCount:47{48data.enforceInterface(DESCRIPTOR);49int _result = this.getCount();50reply.writeNoException();51reply.writeInt(_result);52returntrue;53}54caseTRANSACTION_setCount:55{56data.enforceInterface(DESCRIPTOR);57int _arg0;58_arg0= data.readInt();59this.setCount(_arg0);60reply.writeNoException();61returntrue;62}63}64returnsuper.onTransact(code, data, reply, flags);65}66privatestatic class Proxy implements com.trinea.android.demo.MyAIDLInterface67{68private android.os.IBinder mRemote;69Proxy(android.os.IBinder remote)70{71mRemote= remote;72}73public android.os.IBinderasBinder()74{75return mRemote;76}77public java.lang.StringgetInterfaceDescriptor()78{79returnDESCRIPTOR;80}81publicint getCount() throws android.os.RemoteException82{83android.os.Parcel _data = android.os.Parcel.obtain();84android.os.Parcel _reply = android.os.Parcel.obtain();85int _result;86try {87_data.writeInterfaceToken(DESCRIPTOR);88mRemote.transact(Stub.TRANSACTION_getCount, _data, _reply, 0);89_reply.readException();90_result= _reply.readInt();91}92finally {93_reply.recycle();94_data.recycle();95}96return _result;97}98publicvoid setCount(int count) throws android.os.RemoteException99{100android.os.Parcel _data = android.os.Parcel.obtain();101android.os.Parcel _reply = android.os.Parcel.obtain();102try {103_data.writeInterfaceToken(DESCRIPTOR);104_data.writeInt(count);105mRemote.transact(Stub.TRANSACTION_setCount, _data, _reply, 0);106_reply.readException();107}108finally {109_reply.recycle();110_data.recycle();111}112}113}114staticfinal int TRANSACTION_getCount = (android.os.IBinder.FIRST_CALL_TRANSACTION+ 0);115staticfinal int TRANSACTION_setCount = (android.os.IBinder.FIRST_CALL_TRANSACTION+ 1);116}117publicint getCount() throws android.os.RemoteException;118publicvoid setCount(int count) throws android.os.RemoteException;119}

其中有几个主要的部分:

a. 抽象类Stub,继承Binder实现自定义接口,作用同进程内通信服务中自定义的Binder,客户端通过它对服务进行调用。

b. 静态类Proxy,实现自定义接口,代理模式接收对Stub的调用。

(3) 服务器端定义服务

跟一般的服务定义类似,新建个上面自动生成的MyAIDLInterface.java文件中的Stub的对象,在onBind中返回,代码如下:

View Row Code
1package com.trinea.android.demo;23import android.app.Service;4import android.content.Intent;5import android.os.IBinder;6import android.os.RemoteException;7import android.widget.Toast;89publicclass MyAIDLService extends Service {1011privateint mCount;12privateMyAIDLInterface.Stub myBinder = new MyAIDLInterface.Stub() {1314@Override15publicvoid setCount(int count) throws RemoteException {16mCount= count;17}1819@Override20publicint getCount() throws RemoteException {21return mCount;22}23};2425@Override26publicvoid onCreate() {27mCount= 0;28Toast.makeText(this,"Service onCreate", Toast.LENGTH_SHORT).show();29super.onCreate();30}3132@Override33publicvoid onDestroy() {34Toast.makeText(this,"Service onDestroy", Toast.LENGTH_SHORT).show();35super.onDestroy();36}3738@Override39publicint onStartCommand(Intent intent, int flags, int startId) {40Toast.makeText(this,Integer.toString(mCount),Toast.LENGTH_SHORT).show();41returnsuper.onStartCommand(intent, flags, startId);42}4344publicint getCount() {45return mCount;46}4748/**49* 服务被绑定时调用50* 返回值用于让调用者和服务通信,传入ServiceConnection的public void onServiceConnected(ComponentName name, IBinder service)函数第二个参数51*/52@Override53publicIBinder onBind(Intent intent) {54return myBinder;55}56}

AndroidManifest.xml中注册服务:

View Row Code
1<service android:name=".MyAIDLService">2<intent-filter>3<action android:name="com.trinea.android.demo.remote.MyAIDLServiceAction" />4</intent-filter>5</service>

其中的action name用于后面客户端调用时匹配。

客户端(代码见AndroidAidlClient@GoogleCode):

(1) 拷贝aidl文件

将服务器端的aidl文件拷贝到客户端,客户端也会在gen目录下自动生成同名的java文件。服务器端和客户端文件目录如下:

服务器端目录结构:

服务器端目录结构

客户端目录结构:

客户端目录结构

PS:在运行过程中发现一个小问题,即服务器端服务已经启动,而客户端始终无法BindService成功,报错
ERROR/AndroidRuntime(2104): java.lang.SecurityException: Binder invocation to an incorrect interface。后发现是因为服务器端和客户端aidl文件的包名不同导致生成的java文件不同。

(2) 服务绑定

跟一般的服务绑定类似,只是获取服务的接口不同而已,这里我们通过Stub.asInterface函数获得,代码如下:

View Row Code
1privateMyAIDLInterface binder = null;2privateIntent myAIDLServiceIntent;3privateServiceConnection con = new ServiceConnection() {45@Override6publicvoid onServiceDisconnected(ComponentName name) {78}910@Override11publicvoid onServiceConnected(ComponentName name, IBinder service) {12binder= MyAIDLInterface.Stub.asInterface(service);13}14};1516@Override17publicvoid onCreate(Bundle savedInstanceState) {18super.onCreate(savedInstanceState);19setContentView(R.layout.client);2021boundAIDLServiceBtn= (Button)findViewById(R.id.boundAIDLService);22myAIDLServiceIntent= new Intent("com.trinea.android.demo.remote.MyAIDLServiceAction");23boundAIDLServiceBtn.setOnClickListener(newOnClickListener() {2425@Override26publicvoid onClick(View v) {27boolean result = bindService(myAIDLServiceIntent, con, Context.BIND_AUTO_CREATE);28if (!result) {29binder= null;30Toast.makeText(getApplicationContext(),"服务绑定失败。", Toast.LENGTH_SHORT).show();31}32}33});3435getBoundAIDLServiceProBtn= (Button)findViewById(R.id.getBoundAIDLServicePro);36getBoundAIDLServiceProBtn.setOnClickListener(newOnClickListener() {3738@Override39publicvoid onClick(View v) {40try {41if (binder !=null) {42Toast.makeText(getApplicationContext(),"Service count:" + binder.getCount(),43Toast.LENGTH_SHORT).show();44}else {45Toast.makeText(getApplicationContext(),"请先绑定服务。", Toast.LENGTH_SHORT).show();46}47}catch (RemoteException e) {48e.printStackTrace();49}50}51});5253unboundAIDLServiceBtn= (Button)findViewById(R.id.unboundAIDLService);54unboundAIDLServiceBtn.setOnClickListener(newOnClickListener() {5556@Override57publicvoid onClick(View v) {58if (binder !=null) {59unbindService(con);60binder= null;61}62}63});64}

上面的三个按钮boundAIDLServiceBtn、getBoundAIDLServiceProBtn、unboundAIDLServiceBtn分别用于绑定服务、得到服务中属性值、解除绑定服务。

从上面可以看出我们先定义ServiceConnection对象用于和服务器通信,在onServiceConnected函数中通过MyAIDLInterface.Stub.asInterface(service)返回用户定义的接口对象,通过此对象调用远程接口api。在boundAIDLServiceBtn的onClick函数中绑定服务,在getBoundAIDLServiceProBtn的onClick函数中调用服务的api,在boundAIDLServiceBtn的onClick函数中取消绑定服务,现在我们运行两个项目即可,在客户端绑定服务再获取服务属性显示。

3、生命周期

通过bindService绑定服务,若服务未启动,会先执行Service的onCreate函数,再执行onBind函数,最后执行ServiceConnection对象的onServiceConnected函数,客户端可以自动启动服务。若服务已启动但尚未绑定,先执行onBind函数,再执行ServiceConnection对象的onServiceConnected函数。若服务已绑定成功,则直接返回。这里不会自动调用onStartCommand函数。

通过unbindService解除绑定服务,若已绑定成功,会先执行Service的onUnbind函数,再执行onDestroy函数,注意这里不会执行ServiceConnection对象的onServiceDisconnected函数,因为该函数是在服务所在进程被kill或是crash时被调用。