四大组件之服务:Service
Android Service是Android四大组件之一,它主要用来执行一些不与用户交互的long-run的操作. 注意Service如非特意指定,它仅是运行于该进程的一部分代码而已,另外Service并不是运行在单独线程中,而是主线程中。所以尽量要避免一些ANR的操作。
服务是一种应用程序组件,代表应用程序执行长时间运行的操作而不与用户交互或提供功能供其他应用程序使用的愿望。每个服务类<service> 的包中都必须有一个相应的 声明AndroidManifest.xml。可以使用Context.startService()和 启动服务 Context.bindService()。
请注意,服务与其他应用程序对象一样,在其托管过程的主线程中运行。这意味着,如果您的服务要执行任何占用大量CPU资源(例如MP3播放)或阻塞(例如网络)的操作,则它应产生自己的线程来执行此工作。有关更多信息,请参见“进程和线程”。该IntentService是作为一个标准的实施服务的,有它自己的线程在哪里做它安排其工作。
Service分为如下三类
- Foreground Service:执行一些对于用户来说是可感知的操作,如audio应用使用Foreground Service来播放歌曲。
- Background Service:执行的操作对用户而言是不可感知的。
- Bound Service:主要是提供C/S接口,允许组件与Service进行通信,或者是跨进程的通信。
2、使用Service
继承Service类,自定义Service:
public class ServiceTest extends Service {
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
class MyBinder extends Binder {
}
} 四大组件之一的Service,需在AndroidManifest.xml中注册。使用它一定要在AndroidManifest.xml中声明,在AndroidManifest.xml中声明是为了让PackageManagerService能解析出该Service, 并建立对应的数据结构。
<service
android:name=".ServiceTest"
android:enabled="true"
android:exported="true"/>
2.1、startService
简单的启动Service方式,在Activity中通过Intent和startService()方法 :
Intent intent = new Intent(MainActivity.this, MyService.class);
startService(intent);
多次启动只会执行Service的onCreate()方法一次,创建一个实例。每次startService()都会执行Service中的onStartCommand()方法。这里在onStartCommand()中执行一些操作,耗时操作需要开启线程。不难发现,服务已开启就扔到后台执行不管,执行到什么时候也不知道。而且执行完没有调用stopSelf()方法,后台服务会一直存在。
在Activity中调用stopService()方法,停止服务:
Intent intent = new Intent(MainActivity.this, MyService.class);
stopService(intent);
跳转在不同的Activity中调用startService(),只会执行一次Service的onCreate()方法,创建一个Service实例。任何一个地方stopService都会销毁Service的实例。
2.2、bindService
通常使用方法,在Activity中定义全局变量ServiceConnection和Binder:
//Activity和Service之间建立关联
private MyService.MyBinder mBinder;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//和Service建立连接之后,在主线程中使用mBinder和Service获取数据,通信
mBinder = (MyService.MyBinder) iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
//
}
}; 然后,一般在Activity的onCrea()方法中通过调用bindService()方法,让前台Activity和后台Service建立连接:
Intent intent = new Intent(MainActivity.this, MyService.class);
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
在Activity中的onDestory()方法中去unBindService():
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
} bindService():创建Service实例,并且会调用ServiceConnection类中的onServiceConnected()方法,在该方法中获取到Binder对象,通过该Binder对象,Activity和Service之间进行交互。同时多次调用bindService()只会执行一次onCreate()方法,创建一个Service实例。
unbindService():Service和Activity之间解绑,销毁Service实例。
常规生命周期bindService->unbindService:

2.3、unbindService
unbindService并不会触发Server调用 onServerDisconnected, 相反,当Server端被杀掉或者crash后就会调用 onServerDisconnected 函数通知Client关于Server挂掉了。
如果有多个Client绑定到Server端,并且不是最后一个Client调用unbindService的话,则不会触发Server的onUnbind和onDestroy, 如果是最后一个Client调用unbindService, 则会调用 onUnbind -> onDestroy该Service。原因正如前面红字所示。
2.4、 启动另一个Activity->bindService()->返回
当打开一个Activity,在该Activity中bindService(),返回时,由于Activity的销毁,绑定的Service也会被销毁,执行onDestory()方法。
2.5、 bindService()->再启动另一个Activity->bindService()->返回
先bindService(),在打开启动另一个Activity.B,在另一个Activity.B中bindService(),此时再返回,Activity.B销毁,但是bindService不会销毁。在Activity.B中调用unBindService,也不会调用Service的onDestory(),但是会减少Service的bind连接计数,当减为零的时候会销毁Service。
原因:ActivityManagerService.bringDownServiceLocked方法负责销毁服务,无论stopService或unbindService最终都可能会调用该方法。在真正销毁服务前,会检查和该服务绑定的连接信息,如果有设置过BIND_AUTO_CREATE的链接存在,就不进行销毁。
每次在一个Activity中bindService()时候,都会和这个Activity建立新的连接,ServiceConnection类中的onServiceConnected()方法会执行,但是由于之前已经bindService(),Service实例已存在,不会再次创建。
3、前台服务
将Service推送到前台可以提高Service的存活率,可以使用前台服务的方法:
//把当前服务切换到前台,就是在通知栏显示一个通知。
startForeground(int id, Notification notification)
//停止前台运行,为true清除通知,为false表示不清除通知
stopForeground(boolean removeNotification)
这里有几个坑点:
①调用startForeground方法的第一个参数为通知id,不能为0
//图省事,直接用0,是不会出现任何通知的。纠结了很长时间
startForeground(0, builder.build());
②ID且最好不要与已经定义的普通Notification一样,与正常的通知同用一个id可以“隐藏”掉这个通知。不过可以神不知鬼不觉的在是Service成为前台服务,而不会发出通知。
4、Service ANR
Service虽然是运行在后台的服务,但是也是在主线程,要想执行耗时的任务还得放到子线程中去执行,否则会造成ANR,关于ANR详见《ANR:Application Not Responding》。
如果service运行在Foreground,timeout时间是SERVICE_TIMEOUT即20s,
如果它运行在Background, timeout时间为SERVICE_BACKGROUND_TIMEOUT为200s.
那如果Service在timeout时间内处理完了对应的操作,ActivityThread会调用serviceDoneExecuting通知AMS, service已经完成处理运动了。
serviceDoneExecuting 会在onCreate/onBind/onStartCommand/onUnbind/onDestroy执行后被调用。