卷一:
android中服务是运行在后台的东西,级别与activity差不多。既然说service是运行在后台的服务,那么它就是不可见的,没有界面的东西。你可以启动一个服务Service来播放音乐,或者记录你地理信息位置的改变,或者启动一个服务来运行并一直监听某种动作。
Service和其他组件一样,都是运行在主线程中,因此不能用它来做耗时的请求或者动作。你可以在服务中开一一个线程,在线程中做耗时动作。
那么究竟Service怎么使用呢?
为了只是匮乏的同学,咱们先来点基础知识。
一.基础知识
服务一般分为两种:
1:本地服务, Local Service 用于应用程序内部。在Service可以调用Context.startService()启动,调用Context.stopService()结束。在内部可以调用Service.stopSelf() 或 Service.stopSelfResult()来自己停止。无论调用了多少次startService(),都只需调用一次stopService()来停止。
2:远程服务, Remote Service 用于android系统内部的应用程序之间。可以定义接口并把接口暴露出来,以便其他应用进行操作。客户端建立到服务对象的连接,并通过那个连接来调用服务。调用Context.bindService()方法建立连接,并启动,以调用 Context.unbindService()关闭连接。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载它。
提供给可被其他应用复用,比如定义一个天气预报服务,提供与其他应用调用即可。
那么先来看Service的生命周期吧:如图:
context.startService() ->onCreate()- >onStart()->Service running--调用context.stopService() ->onDestroy()
context.bindService()->onCreate()->onBind()->Service
running--调用>onUnbind() ->onDestroy()从上诉可以知道分别对应本地的,,以及远程的,也对应不同的方式启动这个服务。
二.实战
我们可以定义一个本地服务继承Service,然后在这个服务里播放媒体播放器或者记录地理位置变化。通常有时候我们的Service要与Activity交互,那么可以可以定义一个内部类,返回这个Service,当然我们要考虑到如果是以绑定方式启动服务,那么内部类可以定义为继承Binder,然后返回本地服务,具体代码如下。


package com.dongzi; import android.app.Service; import android.content.Intent; import android.media.MediaPlayer; import android.os.Binder; import android.os.IBinder; import android.util.Log; public class LocalService extends Service { private static final String TAG = "LocalService"; private IBinder binder=new LocalService.LocalBinder(); @Override public IBinder onBind(Intent intent) { return binder; } MediaPlayer mediaPlayer=null; @Override public void onCreate() { Log.i(TAG, "onCreate"); //这里可以启动媒体播放器 // if(mediaPlayer==null) // mediaPlayer=MediaPlayer.create(this, uri); super.onCreate(); } @Override public void onStart(Intent intent, int startId) { Log.i(TAG, "onStart"); super.onStart(intent, startId); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand"); return START_STICKY; } @Override public void onDestroy() { Log.i(TAG, "onDestroy"); super.onDestroy(); } //定义内容类继承Binder public class LocalBinder extends Binder{ //返回本地服务 LocalService getService(){ return LocalService.this; } } }
我们可以从上面知道
//定义内容类继承Binder
public class LocalBinder extends Binder{
//返回本地服务
LocalService getService(){
return LocalService.this;
}
}
可以返回这个服务,然后activity可以通过服务调用服务的方法了。
那么如何启动服务呢?从上面基础知识中,我们知道有2种方法,如下:


//启动服务 private void startCustomService(){ Intent intent=new Intent(this,LocalService.class); startService(intent); }
第2种绑定方式:


LocalService localService=null; //用bindService方法启动服务 private void BinderService(){ Intent intent=new Intent(this,LocalService.class); bindService(intent, new ServiceConnection(){ @Override public void onServiceConnected(ComponentName componentName, IBinder binder) { //调用bindService方法启动服务时候,如果服务需要与activity交互, //则通过onBind方法返回IBinder并返回当前本地服务 localService=((LocalService.LocalBinder)binder).getService(); //这里可以提示用户,或者调用服务的某些方法 } @Override public void onServiceDisconnected(ComponentName componentName) { localService=null; //这里可以提示用户 } }, Context.BIND_AUTO_CREATE); }
在绑定服务的时候,需要一个服务连接对象,ServiceConnection,服务一旦连接,就会调用onServiceConnected方法,我们可以在这个方法里面返回我们的本地服务对象,具体看代码;而在服务断开时候会调用onServiceDisconnected方法,我们可以清理一些服务资源。
注意:Service是不能自己启动的,只有通过 Context 对象 调用startService和bindService方法来启动。也就是说Service不能自己调用的,所以我们看见很多Service都是用Activity调用的。
(1)Context.startService():Service会经历onCreate -> onStart(如果Service还没有运行,则android先调用onCreate()然后调用onStart();如果Service已经运行,则只调用onStart(),所以一个Service的onStart方法可能会重复调用多次);stopService的时候直接onDestroy,如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行。该Service的调用者再启动起来后可以通过stopService关闭Service。
注意,多次调用Context.startservice()不会嵌套(即使会有相应的onStart()方法被调用),所以无论同一个服务被启动了多少次,一旦调用Context.stopService()或者stopSelf(),他都会被停止。补充说明:传递给startService()的Intent对象会传递给onStart()方法。调用顺序为:onCreate --> onStart(可多次调用) --> onDestroy。
(2)Context.bindService():Service会经历onCreate() -> onBind(),onBind将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。这个时候把调用者(Context,例如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用onUnbind -> onDestroyed相应退出,所谓绑定在一起就共存亡了。
补充说明:传递给bindService()的Intent对象会传递给onBind(),传递给unbindService()的Intent对象会传递给onUnbind()方法。调用顺序为:onCreate --> onBind(只一次,不可多次绑定) --> onUnbind --> onDestory。
(3)注意事项:在Service每一次的开启关闭过程中,只有onStart可被多次调用(通过多次startService调用),其他onCreate,onBind,onUnbind,onDestory在一个生命周期中只能被调用一次。还有一点,目前我没有遇到过需要startService和bindService交互使用的情况(我认为不会有这种需求),所以不必去考虑交互的问题,待遇到时再考虑不迟。
(4)BroadcastReceiver只能通过startService启动Service,因为广播本身生命周期很短,bind的话没有意义
PlayService.java是Service类,
包括OnCreate,OnStart,OnDestroy三种方法,
其中OnCreate可有可无,如果有,可以在这里创建与MediaPlayerd的链接。如果无,可以在Onstart里面创建这个链接。
OnCreate在音乐播放周期里面(未OnDestroy)之前,只创建一次,而每按一次播放,OnStart就会被调用一次。
OnStart,里面可以启动播放音乐,
OnDestroy,里面调用停止播放音乐。
如果按了停止按钮,会调用(或者打印Log更直观)“ServiconDestroy“,但是Activity不会退出。
当退出Activity的时候,会相继调用(或者打印Log更直观)"ActivityonStop",和"ActivityOnDestroy"此时,音乐播放是不会停止的,
可以知道,如果想让Service一启动就执行的代码,可以写在Service的OnCreate或OnStart,里面,会在启动Service的时候自动调用,与Activity自动调用OnCreate道理是一样的。
卷二:
Android执行Service有两种方法,一种是startService,一种是bindService。下面让我们一起来聊一聊这两种执行Service方法的区别。
1、生命周期上的区别
执行startService时,Service会经历onCreate->onStartCommand。当执行stopService时,直接调用onDestroy方法。调用者如果没有stopService,Service会一直在后台运行,下次调用者再起来仍然可以stopService。
执行bindService时,Service会经历onCreate->onBind。这个时候调用者和Service绑定在一起。调用者调用unbindService方法或者调用者Context不存在了(如Activity被finish了),Service就会调用onUnbind->onDestroy。这里所谓的绑定在一起就是说两者共存亡了。
多次调用startService,该Service只能被创建一次,即该Service的onCreate方法只会被调用一次。但是每次调用startService,onStartCommand方法都会被调用。Service的onStart方法在API 5时被废弃,替代它的是onStartCommand方法。
第一次执行bindService时,onCreate和onBind方法会被调用,但是多次执行bindService时,onCreate和onBind方法并不会被多次调用,即并不会多次创建服务和绑定服务。
2、调用者如何获取绑定后的Service的方法
onBind回调方法将返回给客户端一个IBinder接口实例,IBinder允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。我们需要IBinder对象返回具体的Service对象才能操作,所以说具体的Service对象必须首先实现Binder对象。
3、既使用startService又使用bindService的情况
如果一个Service又被启动又被绑定,则该Service会一直在后台运行。首先不管如何调用,onCreate始终只会调用一次。对应startService调用多少次,Service的onStart方法便会调用多少次。Service的终止,需要unbindService和stopService同时调用才行。不管startService与bindService的调用顺序,如果先调用unbindService,此时服务不会自动终止,再调用stopService之后,服务才会终止;如果先调用stopService,此时服务也不会终止,而再调用unbindService或者之前调用bindService的Context不存在了(如Activity被finish的时候)之后,服务才会自动停止。
那么,什么情况下既使用startService,又使用bindService呢?
如果你只是想要启动一个后台服务长期进行某项任务,那么使用startService便可以了。如果你还想要与正在运行的Service取得联系,那么有两种方法:一种是使用broadcast,另一种是使用bindService。前者的缺点是如果交流较为频繁,容易造成性能上的问题,而后者则没有这些问题。因此,这种情况就需要startService和bindService一起使用了。
另外,如果你的服务只是公开一个远程接口,供连接上的客户端(Android的Service是C/S架构)远程调用执行方法,这个时候你可以不让服务一开始就运行,而只是bindService,这样在第一次bindService的时候才会创建服务的实例运行它,这会节约很多系统资源,特别是如果你的服务是远程服务,那么效果会越明显(当然在Servcie创建的是偶会花去一定时间,这点需要注意)。
4、本地服务与远程服务
本地服务依附在主进程上,在一定程度上节约了资源。本地服务因为是在同一进程,因此不需要IPC,也不需要AIDL。相应bindService会方便很多。缺点是主进程被kill后,服务变会终止。
远程服务是独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被kill的是偶,该服务依然在运行。缺点是该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。
对于startService来说,不管是本地服务还是远程服务,我们需要做的工作都一样简单。
5、代码实例
startService启动服务
public class LocalService1 extends Service {
/**
* onBind 是 Service 的虚方法,因此我们不得不实现它。
* 返回 null,表示客服端不能建立到此服务的连接。
*/
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onStartCommand(Intent intent, int startId, int flags) {
super.onStartCommand(intent, startId, flags);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
bindService绑定服务
public class LocalService extends Service {
/**
* 在 Local Service 中我们直接继承 Binder 而不是 IBinder,因为 Binder 实现了 IBinder 接口,这样我们可以** 少做很多工作。
*/
public class SimpleBinder extends Binder{
/**
* 获取 Service 实例
* @return
*/
public LocalService getService(){
return LocalService.this;
}
public int add(int a, int b){
return a + b;
}
}
public SimpleBinder sBinder;
@Override
public void onCreate() {
super.onCreate();
// 创建 SimpleBinder
sBinder = new SimpleBinder();
}
@Override
public IBinder onBind(Intent intent) {
// 返回 SimpleBinder 对象
return sBinder;
}
}
上面的代码关键之处,在于 onBind(Intent) 这个方法 返回了一个实现了 IBinder 接口的对象,这个对象将用于绑定Service 的 Activity 与 Local Service 通信。
下面是 Activity 中的代码:
public class Main extends Activity {
private final static String TAG = "SERVICE_TEST";
private ServiceConnection sc;
private boolean isBind;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
sc = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
LocalService.SimpleBinder sBinder = (LocalService.SimpleBinder)service;
Log.v(TAG, "3 + 5 = " + sBinder.add(3, 5));
Log.v(TAG, sBinder.getService().toString());
}
};
findViewById(R.id.btnBind).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
bindService(new Intent(Main.this, LocalService.class), sc, Context.BIND_AUTO_CREATE);
isBind = true;
}
});
findViewById(R.id.btnUnbind).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(isBind){
unbindService(sc);
isBind = false;
}
}
});
}
}
6、在AndroidManifest.xml里Service元素常见选项
android:name ------------- 服务类名
android:label -------------- 服务的名字,如果此项不设置,那么默认显示的服务名则为类名
android:icon -------------- 服务的图标
android:permission ------- 申明此服务的权限,这意味着只有提供了该权限的应用才能控制或连接此服务
android:process ---------- 表示该服务是否运行在另外一个进程,如果设置了此项,那么将会在包名后面加上这段字符串表示另一进程的名字
android:enabled ---------- 表示是否能被系统实例化,为true表示可以,为false表示不可以,默认为true
android:exported --------- 表示该服务是否能够被其他应用程序所控制或连接,不设置默认此项为 false