什么是Service?
Service,俗名服务。在Android系统中,Service与Activity就像一个妈生的,不仅长得像,而且行为(生命周期)也有一些类似。对于Activity来说大家肯定不会陌生,开发Android应用中打过交道最多的莫非就是Activity了,所以今天我们借助Activity来引入讲解Service。Service跟Activity一样是Android的四大组件之一,需要在AndroidManifest清单文件中进行注册。Service不像Activity在前台运行,而且是与之呼应进行后台运行的服务;如果把Activity当成下载软件的用户交互界面,而Service就是那个默默在后台运行的下载线程,所以Service的应用场景就是那些我们不需要它常驻前台但需要它一直在后台工作的时候,例如下载、播放音乐、IM软件监听客户端消息等。
如何启动Service?
启动Service有两种方式,不单调且奢华。在组件中我们可以用类似于Activity的启动方式的startService()来启动Service,用stopService()来关闭启动的服务;当然也可以用比较独特的绑定启动方式bindService()来启动Service这个服务,对应的解绑方法自然就是unbindService()。当然这两种启动方式的不同也意味着Service有着不一样的生命周期调用方法。
例:
定义一个Service类
public class MyService extends Service {
//onBind为必须实现的抽象方法
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public void onRebind(Intent intent) {
super.onRebind(intent);
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
/**
* 构造一个MyBinder类,其继承于Binder,而Binder实现了IBinder的接口
*/
public class MyBinder extends Binder{
//返回Service实例的引用
public MyService getId(){
return MyService.this;
}
}
}
启动方式为startService:
//启动一个Service
Intent intent =newIntent(this, MyService.class);
startService(intent);
//停止Service
stopService(intent);
startService()启动方式的生命周期为:
context.startService() -> onCreate() -> onStart() -> Service 运行中 -> context.stop() -> onDestroy() -> Service 被关闭
启动方式为bindService:
private class MyServiceConnection implements ServiceConnection{
public MyServiceConnection(){}
@Override
public void onServiceConnected(ComponentName arg0, IBinder binder) {
MyService Myservice = ((MyService.MyBinder)binder).getService();
//绑定成功后调用
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
//当服务奔溃时调用
}
}
然后进行bind启动Service
//绑定一个Service
Intent intent =newIntent(this, MyService.class);
MyServiceConnection conn=newMyServiceConnection();
bindService(intent, conn, Context.BIND_AUTO_CREATE);
//解绑Service
unbindService(conn);
当我们执行bindService()后,系统将会调用onCreate()和onBind(),而在onBind()执行完毕后,将会回调ServiceConnection中的onServiceConnected()的方法,同时传回一个实现了IBinder接口的类(一般会选择传回继承至Binder的子类,因为Binder实现了IBinder的接口),然后我们可以通过某些手段(参考上面代码)从IBinder中获取Service的实例。
bindService()启动方式生命周期:
context.bindService() -> onCreate() -> onBind() -> Service 运行中 -> context.unBindService() -> onUnbind() -> onDestroy() -> Service被关闭
某些特殊情况:
情形一:当我们在组件中调用startService()了来启动Service,但是我们并没有调用stopService()来销毁这个服务,那么我们在再次startService()来启动这个服务的时候,并没有调用onCreate()这个回调方法,而是仅仅执行了onStart()。为什么呢?因为在Android系统,每个服务都被设置成单例模式,无论调用多少次startService()来启动Service,在系统永远只会运行一个Service。而onCreate()是Service被创建时被调用的,所以在多次重复启动Service不会调用onCreate(),因为此时系统中已经存在Service这个服务了,所以只会调用onStart()。也就是说在多次启动服务时,onCreate()只会被调用一次,而onStart()可以被调用多次。
情形二:当我们需要把通过startService()启动的Service扫地出门的时候,调用stopService()就能满足我们小小的要求,这时候程序一般如我们预料的执行onDestroy()。但是如果我们并没有祭出stopService()这把宝剑,而是把调用者(如Activity)直接kill掉,那么会执行onDestroy()这个方法吗?答案是不会的,因为自从启动了Service后,该服务就跟调用者撇清关系了,不论调用者生老病死都跟他没半毛钱关系,除非打出stopService()通过意图(intent)把服务回收,当然这一切是建立在该Service是通过startService的方式启动的前提上。
情形三:现在说一下绑定服务(bindService)的情况,同startService的方式一样,如果多次重复启动Service,onCreate()依然只会调用创建Service服务的那个第一次。而对于onBind()则会在每次重复绑定服务的时候被调用,请示在这里onBind()最主要的作用就是将Ibinder传回到绑定者,借用其建立绑定者与Service的联系。而如果有多个绑定的存在,那么执行unbindService()仅仅只会触发onUnbind()而不会触发onDestroy(),只有最后一个绑定者调用unbindService()才会触发服务的onDestroy()的调用。还有一个特别的地方就是,如果销毁绑定者(例如Activity),那么绑定的这个Service会随他而去(执行onUnbid->onDestroy)。
写在最后
1、如果你需要一个在调用者退出后仍然不会被销毁,但同时需要获取他的引用,那么有这么一个小方法。先startService()启动服务,然后bindService绑定该服务,此时可以获取到Service的引用,然后在解绑unbindService,由于先startService所以只会执行onUnbind(),这时由于没有绑定关系,就算该调用者挂掉了,Service依然运行如旧。
2、Service虽然说是后台运行,但是实际上说它仍然是运行在主线程,这里说得运行在主线程是其onCreate、onStart、onBind等生命周期方法运行在主线程,如果这些方法进行诸如下载、读取大文件等耗时工作,会引起主线程的阻塞;所以我们一般会在Service中另起子线程运行我们需要的业务。
3、Service的启动、绑定和停止、解绑应该在对应的调用者相应的生命周期中,例如需要Service贯穿整个Activity,我们可以再onCreate中启动、绑定Service、在onDestroy中停止或解绑Service。如果只是需要在用户前台时运行服务,那么应该在onStart和onStop中进行相应的处理。不建议在onPause和onResume中对于服务进行启动停止的操作,因为这样会可能造成不必要的性能消耗,举个例子,当两个activity同时需要这个服务,那么在前个activity的onPause刚结束服务时,下个activity在onResume马上启动该服务,造成一些恶心的问题。
4、有人认为为什么在bindService的时候不把Ibinder返回到调用者那里,这个是因为启动服务时异步的,在调用bindService的时候是无法获取IBinder并返回的,所以只能在后面通过调用onbind的时候把IBinder扔到onServiceConnected的参数里面。
5、有些人会碰到onServiceConnected()在bindService后并没有被调用,请检查你的onBind()函数,如果返回值为null是不会触发onServiceConnected()的回调的,所以我们要确保onBind返回的是一个实现了IBinder接口的类。
6、我们可以在onUnbind()方法中返回true,这样的话,在我们解绑Service后再次绑定该Service,将会调用onRebind()这个不怎么常见的回调方法,而不会去执行onBind()。