概述
Android系统中的各应用程序都运行在各自的进程中,进程之间通常是无法直接交换数据的。
Android提供了跨进程调用Service的功能,称为AIDL,AIDL全称:android interface define language,
Android接口定义语言。
ADIL相当与两个进程通信的协议,通过这个协议对进程间的通信进行了规范。按照该规范编写
代码即可实现进程间的通信。
AIDL接口文件
跨进程调用服务最关键一步是定义接口文件,该接口文件的扩展名是aidl,
1、在项目的src文件夹下定义一个AIDL接口文件。
2、AIDL接口的内部语法与Java很相似,后面通过例题来演示AIDL接口的语法。
3、.aidl接口文件创建成功后,Android系统会自动生成主文件名相同的.java接口文件。该文件
位于项目的gen文件夹下,该文件不能修改,是Android自动生成的。
操作步骤
以下需要创建两个项目,一个项目是后台服务,一个是启动该后台服务的客户端。通过启动实现两个进程间的通信。
步骤1、创建Service项目,将该项目中的Activity类删除,并从项目清单文件中将该Activity类的注册代码删除。
步骤2~步骤4-创建AIDL接口文件:
创建一个包,在该包中创建一个Java类,该类名为IMyService.java,该类代码如下所示:
package com.tarena.exer11_04.aidl;
interface IMyService {
void play();
void pause();
}
步骤3、将IMyService.java改名为IMyService.aidl,按如下方法操作:定位至以下路径并将IMyService.java
的扩展名改为.adil。
图-6
提示:以上路径是工作空间-workspace下的当前项目的src加包路径
注意:扩展名必须是小写。若资源管理器中不显示扩展名请按以下方法将文件的扩展名显示出来:
单击资源管理器中的“工具栏”菜单,单击其中的“文件夹选项”,显示图-7:
图-7
步骤4、右击项目名,在弹出的菜单中选择Refresh项,刷新项目,如图-8所示:
图-8
图-9是刷新后项目的部分结构示意图:
图-9
其中,蓝框中的IMyService.java是Android根据红框中的IMyService.aidl自动生成的接口。打开该接口文件,
图-10是该接口的部分源代码:
图-10
说明
标注(1)-Stub类是IMyService接口的内部抽象类,该类继承了Binder类,在步骤5中标注(1)所示:在定
义MyBinder类时,不是继承了Binder而是继承了IMyService.Stub类。
标注(2)是Stub类中的asInterface方法,该方法负责将service返回至client的对象转换为IMyService.Stub类型,
这点与进程内部的对象转换不同。
标注(3)指向的两个方法是步骤2定义的IMyService接口中声明的两个方法。这两个方法将在步骤5中
标注(2)所指的代码实现。
提示:
1. aidl文件中不能出现访问限定符,如public。
2. aidl文件(包括所在包)在两个项目中要完全一致。
步骤5、创建图-9中的标注所指-com.tarena.exer11_04.aidlservice包,在该包下创建AIDLService.java类,
该类继承Service类。该类代码如下所示:
public class AIDLService extends Service {
private static final String tag="AIDLService";
private MyBinder mBinder;
@Override
public IBinder onBind(Intent intent) {
Log.i(tag,"service onBind()");
return mBinder;
}
//自定义内部类,注意该类继承的是IMyService.Stub类
public class MyBinder extends IMyService.Stub{
//重写play方法
@Override
public void play() throws RemoteException {
AIDLService.this.play();
}
//重写pause方法
@Override
public void pause() throws RemoteException {
AIDLService.this.pause();
}
}
@Override
public void onCreate() {
super.onCreate();
mBinder=new MyBinder();
Log.i(tag,"service onCreate()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(tag,"serivce onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
//自定义方法
private void play(){
Log.i(tag,"service play()");
}
//自定义方法
private void pause(){
Log.i(tag,"service pause()");
}
}
说明:
1、 ALDLService类中定义了一个内部类MyBinder,该类继承了IMyService.Stub类。这与进程
内的Service定义内部类不同。
2、 标注(3)所指的onCreate方法中创建了MyBinder的一个对象,并在onBind方法中返回该对象。
步骤6、在项目清单文件中注册AIDLService类。
步骤7~步骤11创建客户端项目,该项目用来启动步骤1~步骤6创建的Service。
创建exer11_04-client项目
步骤8、在res/layout/main.xml中创建三个按钮,该布局文件的效果如图-11所示:
图-11
单击图-11中的bind按钮将启动并绑定exer11_04-service项目中的Service。
之后再单击play按钮,将执行exer11_04-service中的AIDLService.play()方法。
单击pause按钮将执行AIDLService.pause()方法,在日志窗口中显示图-12所示的信息
图-12
步骤9、将exer11_04-service项目中创建的IMyService.aidl接口连包一起复制过来,如图-13所示:
图-13
图-13中蓝框中的文件是Android自动在gen文件夹中生成的。
步骤10、打开图-13中标注所指创建MainAct.java,类,该类中用于启动并绑定,
还有调用服务中的play和pause方法的代码如下所示:
@Override
public void onClick(View v) {
Intent intent=new Intent();
switch(v.getId()){
case R.id.btnBind://绑定按钮
//以下启动并绑定另一个进程中的service
intent.setAction(Constant.ACTION_AIDL);
bindService(intent, mConn, BIND_AUTO_CREATE);
break;
case R.id.btnPlay://play按钮
if(mIsBind){
try {
mBinder.play();//调用service中的play方法
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
case R.id.btnPause://pause按钮
if(mIsBind){
try {
mBinder.pause();//调用service中的pause方法
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
}
}
步骤11、为以上代码中红框内的mConn对象编写创建该对象的代码,如下所示:
public class MainAct extends Activity implements OnClickListener{
IMyService mBinder;//接口的一个引用
boolean mIsBind=false;//绑定值为true,未绑定值为false
private ServiceConnection mConn=new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
/*获得另一个进程中的Service传递过来的对象-service,用
* IMyService.Stub.asInterface方法转换该对象,这一点与进程内的通信不同
*/
mBinder=IMyService.Stub.asInterface(service);
mIsBind=true;//设置为true,标志绑定成功
Log.i("MainAcivity","onServiceConnected");
}
};
跨进程绑定服务与本地绑定服务的对比
跨进程调用并绑定服务与绑定本地(同一应用程序内部的服务称为本地服务)服务有所不同。
1、绑定本地服务是:本地的Service通过onBinder方法将装载数据的IBinder对象传递给客户端的ServiceConnection
对象的ServiceConnected方法的第二个参数service。并用Service中的内部类(自定义类)进行转换,从而获得从
服务中返回的对象,通过调用该对象
中的方法或属性值达到与被绑定的服务交换数据和控制该服务的目的。
2、跨进程绑定服务,首先要定义一个扩展名是aidl的接口文件,该接口文件中声明了被绑定服务所提供
的方法。这个接口文件要复制到客户端程序中。
在服务中定义内部类时,不是直接继承Binder类,而是继承接口.Stub类(因Stub类已继承了Binder)。
在客户端的onServiceConnected方法中用IMyService.Stub.asInterface()方法转换服务器端传递过来的对象。