一、Service简介
Android中服务是运行在后台的东西,级别与activity差不多。既然说service是运行在后台的服务,那么它就是不可见的,没有界面的东西。你可以启动一个服务Service来播放音乐,或者记录你地理信息位置的改变,或者启动一个服务来运行并一直监听某种动作。Service和其他组件一样,都是运行在主线程中,因此不能用它来做耗时的请求或者动作。你可以在服务中开一个线程,在线程中做耗时动作。
二、本地服务和远程服务
Android服务分为本地服务(Local Service)和远程服务(Remote Service),这两类服务的区别在于创建服务的客户端(通常为Activity)与所创建的服务是否运行在同一进程中,如下图所示。
图1 图2
如图1,一个Activity调用startService或bindService,创建出服务,若该服务与创建服务的Activity运行在同一进程中,则把这种服务成为本地服务。并且本地服务只能在创建该服务的应用程序内部使用,当应用程序终止时,本地服务也一同终止。
与本地服务不同,远程服务运行在单独的进程中,所以当主应用程序终止时,远程服务仍然会继续运行。若要求在应用程序的主要部分(如Activity)终止时,服务仍要继续运行,执行某些特定任务,此时就该考虑使用远程服务。由于远程服务在程序退出时仍在运行,会继续消耗系统资源,所以在设计远程服务时一定要认真考虑、慎重处理。
三、生命周期
在Android中,服务是应用程序组件,具有一定的生命周期,如下图所示。
图3
从图3我们可以看到,startService()和bindService()两种启动方式的生命周期有所区别,主要有以下几点不同:
1、目的不同,由startService()启动的服务目的只是服务的启动与终止,而bindService()启动的服务是为了服务的远程控制。
2、使用场合不同,使用startService()方法启用服务,调用者与服务之间没有关联,即使调用者退出了,服务仍然运行。使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止。
3、启动流程不同,如果采用startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStartCommand ()方法,如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStartCommand
如果采用bindService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onBind()方法,这个时候调用者和服务绑定在一起,调用者退出了,系统就会先调用服务的onUnbind()方法,接着调用onDestroy()方法。如果调用bindService()方法前服务已经被绑定,多次调用bindService()方法并不会导致多次创建服务及绑定(也就是说onCreate()和onBind()方法并不会被多次调用)。如果调用者希望与正在绑定的服务解除绑定,可以调用unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()-->onDestroy()方法。
基于这两种启动服务方式的特点,我们通常不会单独使用某一种启动方式,经常是将两者结合起来使用。startService服务启动方式比较容易理解,在此不再赘述,下面着重介绍服务的绑定。
四、服务的绑定
本地服务和远程服务的绑定是不一样的,我们分别进行介绍。
(1)本地服务的绑定
在本地服务中,服务与使用服务的客户端程序运行在同一进程中,本地服务的绑定实际上是指客户端程序获取了待绑定服务的一个引用。当绑定完成后,客户端即获取服务引用,通过该引用,客户端即可调用该服务提供的成员变量和各种方法。
我们通过一个demo来了解本地服务如何工作的。程序中主要有两个类,LocalService.java是本地服务类,MainActivity.java是使用服务的Activity,在Manifest文件中注册service
<serviceandroid:name="com.example.localservicedemo.LocalService" ></service>
创建服务类LocalService.java
package com.example.localservicedemo;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class LocalService extends Service {
privatestatic final String TAG = "kingfong";
@Override
publicvoid onCreate() {
//TODO Auto-generated method stub
super.onCreate();
Log.d(TAG,"LocalService-----------onCreate()");
}
@Override
publicint onStartCommand(Intent intent, int flags, int startId) {
//TODO Auto-generated method stub
Log.d(TAG,"LocalService-----------onStartCommand()");
returnsuper.onStartCommand(intent, flags, startId);
}
@Override
publicvoid onDestroy() {
//TODO Auto-generated method stub
super.onDestroy();
Log.d(TAG,"LocalService-----------onDestroy()");
}
publicvoid printLog(){
Log.d(TAG,"LocalService-----------printLog()");
}
@Override
publicIBinder onBind(Intent arg0) {
//TODO Auto-generated method stub
returnmyBinder;
}
publicclass LocalBinder extends Binder{
publicLocalService getService(){
returnLocalService.this;
}
}
privateLocalBinder myBinder = new LocalBinder();
}
1、在MainActivity中设置两个按钮事件,bind和ubind,点击bind调用bindService(),尝试绑定LocalService。
@Override
public void onClick(View v){
switch(v.getId()){
case R.id.bind_service:
bindService();
break;
case R.id.ubind_service:
ubindService();
break;
default:
break;
}
}
public void bindService(){
bindService(newIntent(MainActivity.this,LocalService.class),conn,Context.BIND_AUTO_CREATE);
mIsbind = true;
}
public void ubindService(){
if(mIsbind){
unbindService(conn);
mIsbind = false;
}
}
bindService(Intent service, ServiceConnectionconn,int flag)API的第一个参数是服务的Intent,第二个参数是服务客户端用于处理绑定连接的对象,第三个参数Context.BIND_AUTO_CREATE是一个自动生成本地服务的标记,当带绑定的服务不存在时使用。若LocalService尚未运行,在实施绑定之前先生成LocalService。
2、在待绑定的服务生成之后,调用服务的onBind()回调方法,返回LocalBinder对象,Activity使用该对象与LocalService连接。
@Override
publicIBinder onBind(Intent arg0) {
//TODO Auto-generated method stub
returnmyBinder;
}
publicclass LocalBinder extends Binder{
publicLocalService getService(){
returnLocalService.this;
}
}
privateLocalBinder myBinder = new LocalBinder();
3、如果处理服务绑定的对象(此处指LocalBinder对象)创建成功,就会调用onServiceConnected(ComponentName cname, IBinderservice)方法,第二个参数保存着onBind()生成的LocalBinder对象的引用。
private ServiceConnection conn =new ServiceConnection(){
@Override
publicvoid onServiceConnected(ComponentName cname, IBinder service) {
//TODO Auto-generated method stub
LocalBinderbinder = (LocalBinder)service;
mLocalService= binder.getService();
}
@Override
publicvoid onServiceDisconnected(ComponentName cname) {
//TODO Auto-generated method stub
mLocalService= null;
Log.d(TAG,"Service-------Disconnected");
}
};
4、保存LocalService对象的引用到Activity的mLocalService成员变量中,本地服务绑定完成。
在服务绑定之后,Activity即可通过保存在mLocalService成员变量中的LocalService对象的引用,访问服务的成员变量和相关方法。如调用LocalService中的printLog()方法。
mLocalService.printLog();
根据以上详细分析可总结为如下过程:
bindService()-------->onBind(),传递Binder对象--------> onServiceConnected()-------->通过传递的Binder对象获取服务对象-------->调用service中定义的成员变量和方法
(2)远程服务的绑定
对远程服务而言,其在单独的进程中,Activity若想控制服务,必须使用IPC机制(后续介绍)。远程服务绑定是指设置相关的连接设置,以便运行Binder IPC。
在Binder IPC通信中,服务于activity交换数据时,需要经历Marshlling/Unmarshalling这一过程,为此,就要使用AIDL(Android Interface Definition Language,android接口定义语言),用于约束两个进程间的通信规则,共编译器生成代码,用来实现android设备上两个进程间的通信(Interprocess Communication, IPC)。
下面我们仍然通过一个demo来分析远程服务的绑定流程,远程服务的代码构成与本地服务是有所区别的,除了RemoteService.java和MainActivity.java之外,还有IRemoteService.aidl以及由该文件自动生成的IRemoteService.java文件。
在IRemoteService.aidl中定义了一个IRemoteService接口和count()方法
package com.example.remoteservicedemo;
interface IRemoteService
{
void count();
}
AIDL文件创建好后,刷新下工程,在gen目录下可以看到和AIDL名一样的java文件,具体内容不详细介绍。
另外,在manifest文件中也有所区别,如下所示
<serviceandroid:name="com.example.remoteservicedemo.RemoteService"android:process=":remote" android:exported="false">
<intent-filter>
<actionandroid:name="com.example.remoteservicedemo.IRemoteService"/>
</intent-filter>
</service>
android:process=":remote",代表在应用程序里,当需要该service时,会自动创建新的进程。而如果是android:process="remote",没有“:”分号的,则创建全局进程,不同的应用程序共享该进程。
1、 MainActivity:请求RemoteService连接
在MainActivity同样设置两个按钮事件,点击bind,执行bindService()API
publicvoid bindService(){
bindService(newIntent(IRemoteService.class.getName()),conn,Context.BIND_AUTO_CREATE);
mIsbound= true;
}
publicvoid unbindService(){
if(mIsbound){
unbindService(conn);
mIsbound= false;
Log.d(TAG,"unbindService()");
}
}
2、RemoteService:具体实现服务方法,并提供用于通信的Binder对象
服务启动后,会根据服务的生命周期,依次调用onCreate、onBind方法。onBind方法的主要作用是生成用于处理Binder IPC的Binder对象,并将其返回给系统。
服务的绑定对象是由IRemoteService.java的IRemoteService.Stub类生成的,实现定义在IRemoteService接口中的count()方法。
@Override
publicIBinder onBind(Intent arg0) {
//TODO Auto-generated method stub
returnmBinder;
}
privatefinal IRemoteService.Stub mBinder = new IRemoteService.Stub() {
@Override
public void count() throws RemoteException {
//TODO Auto-generated method stub
mHandler.sendMessage(mHandler.obtainMessage(PRINT_LOG));
}
};
3、MainActivity:生成执行服务于Binder IPC的代理对象
与本地服务类似,不同的是将Binder对象传递给IRemoteService.Stub.asInterface函数,并使用它生成与RemoteService服务绑定在一起的服务代理对象IRemoteService.Stub.Proxy,最后将其保存到mService成员变量中
private ServiceConnection conn =new ServiceConnection(){
@Override
publicvoid onServiceConnected(ComponentName cname, IBinder service) {
//TODO Auto-generated method stub
mService= IRemoteService.Stub.asInterface(service);
}
至此,RemoteService的IRemoteService接口的绑定就完成了,activity可以通过保存在mService中的服务代理对象,调用服务中的方法。
4、MainActivity:使用服务代理对象,调用服务中的count()方法
if(mService!=null){
try{
mService.count();
}catch(RemoteExceptione){}
}
5、Binder IPC:服务代理对象(IRemoteService.Stub.Proxy)向服务Binder对象(IRemoteService.Stub)传递Binder IPC数据
IRemoteService.Stub.Proxy远程代理对象通过Binder IPC向IRemoteService.Stub服务Binder对象传递数据,以处理4中对count()代理方法的调用。
6、RemoteService:调用RemoteService的count() stub方法
IRemoteService.Stub服务Binder对象获取Binder IPC数据后,会调用2中实现的count() stub方法,将服务进程的ID返回给activity。
至此,我们对远程服务的绑定及绑定后调用相关接口特定方法做了简单介绍。
参考文献:
【1】金泰延、宋亨周、朴知勋、李白、林起永著 Android框架揭秘