首页

Service与跨进程调用及AIDL接口

搜索

用BroadcastReceiver控制Service

概述

通过在Service类的内部定义BroadcastReceiver对象,接收外部发送的广播,也可以达到控制Service的目的。

操作步骤

步骤1、自定义Service的子类。

步骤2、在该类中定义一个内部类,该类是BroadcastReceiver类的子类。

步骤3、在Service类的onCreate方法中注册该广播接收者类,并设置action值。

步骤4、在Activity中启动或绑定Service。

步骤5、在Activity中发送广播并传递数据,让Service中的广播接收者收到广播,根

据接收到的数据执行不同的业务逻辑代码。

示例

创建项目exer11_02,在该类中创建MyService类,该类是Service的子类,在该类中定义一个

内部类,该内部类是BroadcastReceiver的子类。

图-5

在MainAct中首先启动Service类,然后向该类发送两个广播,第一个广播是play,在日志窗口中将

显示图-5中红框中的第一行信息,显示Service中定义的play方法被调用。

第二个广播是pause,在日志窗口中将显示图-5红框中的第二行信息,显示Service中定义的pause方法被调用。

步骤1、创建布局界面,该段代码省略。

步骤2、创建Constant.java类,该类是一个工具类,存放播放与暂停两个动作的Action的值,代码如下所示:

package com.tarena.exer11_02;

public class Constant {

public static final String ACTION_PLAY="com.tarena.exer11_02.ACTION_PLAY";

public static final String ACTION_PAUSE="com.tarena.exer11_02.ACTION_PAUSE";

public static final String ACTION_MYSERVICE=

"com.tarena.exer11_02.ACTION_MYSERVICE"; 

}

步骤3、创建MyService.java类,该类继承自Service类,代码如下所示:

public class MyService extends Service {

private String tag="MyService";//日志窗口中输出的标签

//因不涉及绑定,所以onBind方法返回的对象为null

@Override

public IBinder onBind(Intent intent) {

// TODO Auto-generated method stub

return null;

}

//服务创建时调用本方法

@Override

public void onCreate() {

super.onCreate();

Log.i(tag,"MyService onCreate()");

//以下创建并注册一个广播接收者对象

MyReceiver myReceiver=new MyReceiver();

//以下三行代码设置两个Action值

IntentFilter filter=new IntentFilter();

filter.addAction(Constant.ACTION_PAUSE);

filter.addAction(Constant.ACTION_PLAY);

registerReceiver(myReceiver, filter);//用代码的方式向系统注册

}

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

Log.i(tag,"MyService onStartCommand()");

return super.onStartCommand(intent, flags, startId);

}

//自定义方法

private void play(){

Log.i(tag,"MyService play()");

}

//自定义 方法

private void pause(){

Log.i(tag,"MyService pause()");

}

//自定义广播接收者类

private class MyReceiver extends BroadcastReceiver{

//在收到广播时触发本方法

@Override

public void onReceive(Context context, Intent intent) {

//若Action的值Constant.ACTION_PLAY,则调用play()

if(Constant.ACTION_PLAY.equals(intent.getAction())){

MyService.this.play();

//若Action的值是Constant.ACTION_PAUSE,则调用pause()

}else if(Constant.ACTION_PAUSE.equals(intent.getAction())){

MyService.this.pause();

}

}

}

}

步骤4、以下是MainAct.java类的代码,该代码负责启动Service并向Service发送两种类型的广播。

public class MainAct extends Activity implements OnClickListener{

    Intent mIntent;

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        Button btnPlay=(Button)findViewById(R.id.btnPlay);

        Button btnPause=(Button)findViewById(R.id.btnPause);

        btnPause.setOnClickListener(this);

        btnPlay.setOnClickListener(this);

        //启动Service

        mIntent=new Intent(Constant.ACTION_MYSERVICE);

    startService(mIntent);

    }

@Override

public void onClick(View v) {

switch(v.getId()){

case R.id.btnPlay://播放按钮

//发送action值是Constant.ACTION_PLAY的广播

mIntent.setAction(Constant.ACTION_PLAY);

sendBroadcast(mIntent);

break;

case R.id.btnPause:

//发送action值是Constant.ACTION_PAUSE的广播

mIntent.setAction(Constant.ACTION_PAUSE);

sendBroadcast(mIntent);

break;

}

}

}

使用绑定控制Service

概述

在Activity绑定Service时,通过返回一个Binder子类的对象(该子类中定义了操作Service的方法)。

这样在Activity中,通过调用该对象的方法,实现对Service的控制。

操作步骤

步骤1、创建Service的一个子类,在该类中定义一个内部类,例如MyBinder,该类是Binder的子类。

步骤2、在内部类中定义若干操作Service的方法。

步骤3、在onBind方法中返回一个该内部类的对象。

步骤4、在Activity中绑定Service,在ServiceConnection的ServiceConnected方法中将第二个参数转换

为该Service的内部类的类型并赋值给一个对象。

步骤5、调用该对象中的方法操作实现对Service的操作。

示例

步骤1、以下是MyService类中关键代码:

public class MyService extends Service {

private String tag="MyService";

MyBinder mBinder;

public class MyBinder extends Binder{

public void print(String text){

MyService.this.print(text);

}

}

@Override

public IBinder onBind(Intent intent) {

Log.i(tag,"MyService onBind()");

return new MyBinder();

}

说明:

1、该类中定义了一个内部类:MyBinder,该类中定义了一个名为print的方法,

该方法调用了MyService类中的一个自定义的方法print。

2、在onBind方法中,创建并返回了一个Mbinder对象。

步骤2、以下是Activity中的关键代码:

    MyService.MyBinder mBinder;

    private ServiceConnection mConn=new ServiceConnection() {

@Override

public void onServiceDisconnected(ComponentName name) {

}

//绑定成功时触发本方法

@Override

public void onServiceConnected(ComponentName name, IBinder service) {

//获得MyService.bind()方法返回的对象(service)

mBinder=(MyService.MyBinder)service;

}

};

说明:当Activity与MyService绑定成功时,执行OnServiceConnected,并将onBind方法返回的对象传

递给service对象,则mBinder获得了onBind方法返回的对象。

步骤3、这是Activity中调用mBinder对象中方法的代码:

case R.id.btnBindService://绑定服务

intent.setClass(this, MyService.class);

bindService(intent, mConn, BIND_AUTO_CREATE);

mIsBind=true;

break;

case R.id.btnUnbindService://解除与服务的绑定

intent.setClass(this, MyService.class);

if(mIsBind){

mBinder.print("unBind");

unbindService(mConn);

mIsBind=false;

}

break;

说明:当解除绑定时,执行红框中的代码,调用mBinder.print方法。

跨进程调用服务

概述

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()方法转换服务器端传递过来的对象。

 

 

 

上一页 下一页