Android Service最全面的解析
2017-12-12 11:06
561 查看
android四大组件之一:Service。**使用Service可以在后台长时间执行一段操作,一般不与用户产生UI交互。Service可以由其他的android组件启动,并在后台独立运行,即使启动该Service的组件已被销毁,也不影响Service的运行;另外Service也可以与一个组件绑定启动,这时该Service就与该组件“共存亡”;最后Service可以与一个组件进行进程间的IPC通讯,见这篇博客:
> http://blog.csdn.net/shangming150/article/details/78086709
bindService(Intent service, ServiceConnection conn,int flags):一个组件通过该方法,与一个Service绑定,此时该Service与绑定它的组件“共存亡”;一旦绑定它的组件被销毁,那么Service也会被停止并destroy。并且这种绑定方式启动的Service可以与该组件进行交互;多个组件可与一个Service绑定,当最后一个与该Service绑定的组件被销毁后,该Service才被destroy。
startService&bindService:这种运行方式是上面两种运行方式的结合产物,即Service以startService启动之后,再使用bindService方式与某一组件进行绑定。这样在绑定的组件被销毁后,该Service依然能够独立运行而不被销毁
无论通过那种方式启动Service(start、bind、start&bind),任何组件(甚至其他应用的组件)都可以使用Service。并通过Intent传递参数。当然,您也可以将Service在manifest文件中配置成私有的,不允许其他应用访问。
需要注意:Service是运行在主线程中(A service runs in the main thread of its hosting process)的,它不是新的线程,也不是新的进程。所以若需要在Service中执行较为耗时的操作(如播放音乐、执行网络请求等),需要在Service中创建一个新的线程来执行,以防止ANR的发生。
为了创建Service,需要继承
receiver 等。该方法是Service中的最后一个回调。
如果某个组件通过调用
如果某个组件通过调用
当系统内存低时,系统将强制停止Service的运行;若Service绑定了正在与用户交互的activity,那么该Service将不大可能被系统kill( less likely to be killed)。如果创建的是前台Service,那么该Service几乎不会被kill(almost never be killed)。否则,当创建了一个长时间在后台运行的Service后,系统会降低该Service在后台任务栈中的级别——这意味着它容易被kill(lower its position in the list
of background tasks over time and the service will become highly susceptible to killing),所以在开发Service时,需要使Service变得容易被restart,因为一旦Service被kill,再restart它需要其资源可用时才行(restarts it as soon as resources become available again ),当然这也取决于
在manifest文件中注册service的方式如下:
除此之外,在
process in which the service should run)。
!请注意:为了保证应用的安全,请使用显式Intent启动或绑定一个Service,请不要在
若不确定该启动哪个Service,那么可以在
the component name from the Intent),但必须调用Intent的
sufficient disambiguation for the target service)。
注:
将
其他组件调用
比如说,Activity需要向在线数据库中上传数据,那么可以调用
一般使用如下两种方式创建一个start Service:
继承
继承
在大多数情况下,start Service并不会同时处理多个请求(don’t need to handle multiple requests simultaneously),因为处理多线程较为危险(a dangerous multi-threading scenario),所以继承
使用
默认在子线程中处理回传到
在重写的
当所有请求处理完成后,自动停止service,无需手动调用
默认实现了
默认实现了
综上所述,您只需重写
如果您还希望在
比如,要实现
除
如果您需要在Service中执行多线程而不是处理一个请求队列(perform multi-threading instead of processing start requests through a work queue),那么需要继承Service类,分别处理每个Intent。
在Service中执行操作时,处理每个请求都需要开启一个线程,并且同一时刻一个线程只能处理一个请求( for each start request, it uses a worker thread to perform the job and processes only one request at a time)。
注意到
intent。这避免了在不必要的时候运行service,您的应用也可以restart任何未完成的操作。
not redeliver the last intent)。相反,系统回调
pending intents都会被轮流传递。该模式适合做一些类似下载文件的操作。
若需要启动Service,见下面所示:
多个启动Service的请求可能导致
一个启动的Service必须管理自己的生命周期。系统不会主动stop或destroy一个运行的Service,除非系统内存紧张,否则,执行完
一旦调用了上述两种方法之一,系统会尽快destroy该Service(as soon as possible)。
若系统正在处理多个调用
start request)。为了避免这个问题,您可以调用
on the most recent start request)。这就是说,当调用
为了节省内存和电量,当Service完成其工作后将其stop很有必要。如有必要,可以在其他组件中调用
通过其他组件调用
当其他组件需要与Service交互或者需要跨进程通信时,可以创建一个bound Service。
为创建一个bound Service,必须重写
为创建一个bound Service,必须定义一个接口 ,该接口指定组件与Service如何通信。定义的接口在组件与Service之间,且必须实现
多个组件可同时与Service绑定,当组件与Service交互结束后,可调用
运行中的Service可以通过Toast Notifications 或 Status
Bar Notifications 向用户发送通知。Toast是一个可以短时间弹出的提醒框。二Status Bar是顶部状态栏中出现的太有图标的信息,用户可以通过下拉状态栏获得具体信息并执行某些操作(如启动Activity)。
通常,Status Bar用于通知某些操作已经完成,如下载文件完成。当用户下拉状态栏后,点击该通知,可获取详细内容,如查看该下载的文件。
前台Service用于动态通知消息,如天气预报。该Service不易被kill。前台Service必须提供status bar,只有前台Service被destroy后,status bar才能消失。
举例来说,一个播放音乐的Service必须是前台Service,只有这样用户才能确知其运行状态。为前台Service提供的status bar可以显示当前音乐的播放状态,并可以启动播放音乐的Activity。
调用
bar,示例如下:
!注意:为
调用
the status bar notification as well)。该方法并不停止Service,如果停止正在前台运行的Service,那么notification 也一并被清除。
从Service的启动到销毁,有两种路径:
A started service:需手动停止
A bound service:可自动停止
如下图所示 :
这两条路径并不是毫不相干的:当调用
,调用
这些生命周期方法在使用时无需调用各自的父类方法。
在两条生命周期路径中,都包含了两个嵌套的生命周期:
完整生命周期( entire lifetime ):从
无论是
活动生命周期(active lifetime):从
若是Start Service,那么Service的活动生命周期结束就意味着其完整生命周期结束 (the active lifetime ends the same time that the entire lifetime ends),即便
Service,那么当
!请注意:针对Start Service,由于Service中没有类似
> http://blog.csdn.net/shangming150/article/details/78086709
Service运行方式:
startService(Intent intent):一个组件(例如Activity)通过startService方式启动一个Service,一旦启动,Service将一直运行在后台(run in the background indefinitely),即便启动Service的组件已被销毁。这种启动方式下,一般情况Service并不与启动它的组件进行交互,而是独立执行一个操作,如上传、下载等网络操作,操作结束之后自己调用stopSelf或其他组件调用stopService(Intent intent)结束该Service,负责该Service分配的资源会一直存在;bindService(Intent service, ServiceConnection conn,int flags):一个组件通过该方法,与一个Service绑定,此时该Service与绑定它的组件“共存亡”;一旦绑定它的组件被销毁,那么Service也会被停止并destroy。并且这种绑定方式启动的Service可以与该组件进行交互;多个组件可与一个Service绑定,当最后一个与该Service绑定的组件被销毁后,该Service才被destroy。
startService&bindService:这种运行方式是上面两种运行方式的结合产物,即Service以startService启动之后,再使用bindService方式与某一组件进行绑定。这样在绑定的组件被销毁后,该Service依然能够独立运行而不被销毁
无论通过那种方式启动Service(start、bind、start&bind),任何组件(甚至其他应用的组件)都可以使用Service。并通过Intent传递参数。当然,您也可以将Service在manifest文件中配置成私有的,不允许其他应用访问。
需要注意:Service是运行在主线程中(A service runs in the main thread of its hosting process)的,它不是新的线程,也不是新的进程。所以若需要在Service中执行较为耗时的操作(如播放音乐、执行网络请求等),需要在Service中创建一个新的线程来执行,以防止ANR的发生。
Service基础(The Basics)
为了创建Service,需要继承Service类。并重写它的回调方法,这些回调方法反应了Service的生命周期,并提供了绑定Service的机制。最重要的Service的生命周期回调方法如下所示:
onStartCommand():当其他组件调用
startService()方法请求启动Service时,该方法被回调。一旦Service启动,它会在后台独立运行。当Service执行完以后,需调用
stopSelf() 或 stopService()方法停止Service。(若您只希望bind Service,则无需调用这些方法)
onBind():当其他组件调用
bindService()方法请求绑定Service时,该方法被回调。该方法返回一个
IBinder接口,该接口是Service与绑定的组件进行交互的桥梁。若Service未绑定其他组件,该方法应返回null。
onCreate():当Service第一次创建时,回调该方法。该方法只被回调一次,并在
onStartCommand() 或 onBind()方法被回调之前执行。若Service处于运行状态,该方法不会回调。
onDestroy():当Service被销毁时回调,在该方法中应清除一些占用的资源,如停止线程、接触绑定注册的监听器或broadcast
receiver 等。该方法是Service中的最后一个回调。
如果某个组件通过调用
startService()启动了Service(系统会回调
onStartCommand()方法),那么直到在Service中手动调用
stopSelf()方法、或在其他组件中手动调用
stopService()方法,该Service才会停止。
如果某个组件通过调用
bindService()绑定了Service(系统不会回调
onStartCommand()方法),只要该组件与Service处于绑定状态,Service就会一直运行,当Service不再与组件绑定时,该Service将被destroy。
当系统内存低时,系统将强制停止Service的运行;若Service绑定了正在与用户交互的activity,那么该Service将不大可能被系统kill( less likely to be killed)。如果创建的是前台Service,那么该Service几乎不会被kill(almost never be killed)。否则,当创建了一个长时间在后台运行的Service后,系统会降低该Service在后台任务栈中的级别——这意味着它容易被kill(lower its position in the list
of background tasks over time and the service will become highly susceptible to killing),所以在开发Service时,需要使Service变得容易被restart,因为一旦Service被kill,再restart它需要其资源可用时才行(restarts it as soon as resources become available again ),当然这也取决于
onStartCommand()方法返回的值,这将在后续介绍。
在manifest文件中注册service(Declaring a service in the manifest)
在manifest文件中注册service的方式如下:除此之外,在
<service>标签中还可以配置其他属性,比如,需要启动该service所需的权限、该service应运行在哪个进程中 等( permissions required to start the service and the
process in which the service should run)。
android:name属性是唯一不可缺省的,它指定了Service的全限定类名。一旦发布了应用,该类名将不可更改。
!请注意:为了保证应用的安全,请使用显式Intent启动或绑定一个Service,请不要在
<service>标签中配置intent-filter。
若不确定该启动哪个Service,那么可以在
<service>中配置intent-filter,并在Intent中排除该Service(supply intent filters for your services and exclude
the component name from the Intent),但必须调用Intent的
setPackage()方法,来为启动的service消除歧义(provides
sufficient disambiguation for the target service)。
注:
setPackage()方法传入一个String参数,代表一个包名。该方法表示该Intent对象只能在传入的这个包名下寻找符合条件的组件,若传入null,则表示可以在任意包下寻找。
将
android:exported属性设为false,表示不允许其他应用程序启动本应用的组件,即便是显式Intent也不行(even when using an explicit intent)。这可以防止其他应用程序启动您的service组件。
使用start方式启动Service(Creating a Started Service)
其他组件调用startService()方法可以启动一个Service,接着,Service会回调
onStartCommand()生命周期方法。
startService()方法中传入一个Intent参数,用于显式指定目标Service的名字,并携带data以供Service使用,该Intent参数将回传至
onStartCommand()方法中。
比如说,Activity需要向在线数据库中上传数据,那么可以调用
startService()启动一个Service,并将数据传入Intent的data中,接着,
onStartCommand()方法会接收这个Intent并开启一个线程将数据上传至网络,当数据上传完成后,该Service将停止并被destroy。
一般使用如下两种方式创建一个start Service:
继承
Service类:请务必在Service中开启线程来执行耗时操作,因为Service运行在主线程中。
继承
IntentService类:
IntentService继承于
Service,若Service不需要同时处理多个请求,那么使用
IntentService将是最好选择:您只需要重写
onHandleIntent( f21b )方法,该方法接收一个回传的Intent参数,您可以在方法内进行耗时操作,因为它默认开启了一个子线程,操作执行完成后也无需手动调用
stopSelf()方法,
onHandleIntent()会自动调用该方法。
继承IntentService类(Extending the IntentService class)
在大多数情况下,start Service并不会同时处理多个请求(don’t need to handle multiple requests simultaneously),因为处理多线程较为危险(a dangerous multi-threading scenario),所以继承IntentService类带创建Service是个不错选择。
使用
IntentService的要点如下:
默认在子线程中处理回传到
onStartCommand()方法中的Intent;
在重写的
onHandleIntent()方法中处理按时间排序的Intent队列,所以不用担心多线程(multi-threading)带来的问题。
当所有请求处理完成后,自动停止service,无需手动调用
stopSelf()方法;
默认实现了
onBind()方法,并返回null;
默认实现了
onStartCommand()方法,并将回传的Intent以序列的形式发送给
onHandleIntent(),您只需重写该方法并处理Intent即可。
综上所述,您只需重写
onHandleIntent()方法即可,当然,还需要创建一个构造方法,示例如下:
如果您还希望在
IntentService的 继承类中重写其他生命周期方法,如
onCreate()、onStartCommand() 或 onDestroy(),那么请先调用各自的父类方法以保证子线程能够正常启动。
比如,要实现
onStartCommand()方法,需返回其父类方法:
除
onHandleIntent()外,
onBind()方法也无需调用其父类方法。
继承Service类(Extending the Service class)
如果您需要在Service中执行多线程而不是处理一个请求队列(perform multi-threading instead of processing start requests through a work queue),那么需要继承Service类,分别处理每个Intent。在Service中执行操作时,处理每个请求都需要开启一个线程,并且同一时刻一个线程只能处理一个请求( for each start request, it uses a worker thread to perform the job and processes only one request at a time)。
注意到
onStartCommand()返回一个整形变量,该变量必须是下列常量之一:
START_NOT_STICKY:若执行完
onStartCommand()方法后,系统就kill了service,不要再重新创建service,除非系统回传了一个pending
intent。这避免了在不必要的时候运行service,您的应用也可以restart任何未完成的操作。
START_STICKY:若系统在
onStartCommand()执行并返回后kill了service,那么service会被recreate并回调
onStartCommand()。dangerous不要重新传递最后一个Intent(do
not redeliver the last intent)。相反,系统回调
onStartCommand()时回传一个空的Intent,除非有 pending intents传递,否则Intent将为null。该模式适合做一些类似播放音乐的操作。
START_REDELIVER_INTENT:若系统在
onStartCommand()执行并返回后kill了service,那么service会被recreate并回调
onStartCommand()并将最后一个Intent回传至该方法。任何
pending intents都会被轮流传递。该模式适合做一些类似下载文件的操作。
启动服务(Starting a Service)
若需要启动Service,见下面所示:startService(intent)方法将立即返回,并回调
onStartCommand()(请不要手动调用该方法),若该Service未处于运行状态,系统将首先回调
onCreate(),接着再回调
onStartCommand()。若您希望Service可以返回结果,那么需要通过调用
getBroadcast 返回的PendingIntent启动Service(将PendingIntent包装为Intent),service可使用broadcast 传递结果。
多个启动Service的请求可能导致
onStartCommand()多次调用,但只需调用
stopSelf() 、 stopService()这两个方法之一,就可停止该服务。
停止服务(Stopping a service)
一个启动的Service必须管理自己的生命周期。系统不会主动stop或destroy一个运行的Service,除非系统内存紧张,否则,执行完onStartCommand()方法后,Service依然运行。停止Service必须手动调用
stopSelf()(在Service中)或调用
stopService()(在启动组件中)。
一旦调用了上述两种方法之一,系统会尽快destroy该Service(as soon as possible)。
若系统正在处理多个调用
onStartCommand()请求,那么在启动一个请求时,您不应当在此时停止该Service(you shouldn’t stop the service when you’re done processing a
start request)。为了避免这个问题,您可以调用
stopSelf(int)方法,以确保请求停止的Service时最新的启动请求( your request to stop the service is always based
on the most recent start request)。这就是说,当调用
stopSelf(int)方法时,传入的ID代表启动请求(该ID会传递至
onStartCommand()),该ID与请求停止的ID一致。则如果在调用
stopSelf(int)之前,Service收到一个新的Start请求,ID将无法匹配,Service并不会停止。
为了节省内存和电量,当Service完成其工作后将其stop很有必要。如有必要,可以在其他组件中调用
stopService()方法,即便Service处于绑定状态,只要它回调过
onStartCommand(),也应当主动停止该Service。
创建绑定Service(Creating a Bound Service)
通过其他组件调用bindService()方法可以绑定一个Service以保持长连接(long-standing connection),这时一般不允许其他组件调用
startService()启动Service。
当其他组件需要与Service交互或者需要跨进程通信时,可以创建一个bound Service。
为创建一个bound Service,必须重写
onBind()回调,该方法返回一个
IBinder接口。该接口时组件与Service通信的桥梁。组件调用
bindService()与Service绑定,该组件可获取
IBinder接口,一旦获取该接口,就可以调用Service中的方法。一旦没有组件与Service绑定,系统将destroy它,您不必手动停止它。
为创建一个bound Service,必须定义一个接口 ,该接口指定组件与Service如何通信。定义的接口在组件与Service之间,且必须实现
IBinder接口。这正是
onBind()的返回值。一旦组件接收了
IBinder,组件与Service便可以开始通信。
多个组件可同时与Service绑定,当组件与Service交互结束后,可调用
unbindService()方法解绑。bound Service比start Service要复杂,故我将在后续单独翻译。
向用户发送通知(Sending Notifications to the User)
运行中的Service可以通过Toast Notifications 或 StatusBar Notifications 向用户发送通知。Toast是一个可以短时间弹出的提醒框。二Status Bar是顶部状态栏中出现的太有图标的信息,用户可以通过下拉状态栏获得具体信息并执行某些操作(如启动Activity)。
通常,Status Bar用于通知某些操作已经完成,如下载文件完成。当用户下拉状态栏后,点击该通知,可获取详细内容,如查看该下载的文件。
运行前台Service(Running a Service in the Foreground)
前台Service用于动态通知消息,如天气预报。该Service不易被kill。前台Service必须提供status bar,只有前台Service被destroy后,status bar才能消失。举例来说,一个播放音乐的Service必须是前台Service,只有这样用户才能确知其运行状态。为前台Service提供的status bar可以显示当前音乐的播放状态,并可以启动播放音乐的Activity。
调用
startForeground()可以启动前台Service。该方法接收两个参数,参数一是一个int型变量,用户指定该通知的唯一性标识,而参数而是一个
Notification用于配置status
bar,示例如下:
!注意:为
startForeground()设置的ID必须是0。
调用
stopForeground()来移除(remove)前台Service。该方法需传入一个boolean型变量,表示是否也一并清除status bar上的notification(indicating whether to remove
the status bar notification as well)。该方法并不停止Service,如果停止正在前台运行的Service,那么notification 也一并被清除。
Service生命周期(Managing the Lifecycle of a Service)
从Service的启动到销毁,有两种路径:A started service:需手动停止
A bound service:可自动停止
如下图所示 :
这两条路径并不是毫不相干的:当调用
startService()start一个Service后,您仍可以bind该Service。比如,当播放音乐时,需调用
startService()启动指定播放的音乐,当需要获取该音乐的播放进度时,有需要调用
bindService(),在这种情况下,知道Service被unbind
,调用
stopService() 或stopSelf()都不能停止该Service。
实现Service的生命周期回调(Implementing the lifecycle callbacks)
这些生命周期方法在使用时无需调用各自的父类方法。
在两条生命周期路径中,都包含了两个嵌套的生命周期:
完整生命周期( entire lifetime ):从
onCreate()被调用到
onDestroy()返回。与Activity类似,一般在
onCreate()中做一些初始化工作,而在
onDestroy()做一些资源释放工作。如,若Service在后台播放一个音乐,就需要在
onCreate()方法中开启一个线程启动音乐,并在
onDestroy()中结束线程。-
无论是
startService() 还是 bindService()启动Service,
onCreate() 和 onDestroy()均会被回调。
活动生命周期(active lifetime):从
onStartCommand() 或 onBind()回调开始。由相应的
startService() 或 bindService()调用。
若是Start Service,那么Service的活动生命周期结束就意味着其完整生命周期结束 (the active lifetime ends the same time that the entire lifetime ends),即便
onStartCommand()返回后,Service仍处于活动状态;若是bound
Service,那么当
onUnbind()返回时,Service的活动生命周期结束。
!请注意:针对Start Service,由于Service中没有类似
onStop()的回调,所以在调用
stopSelf() 或 stopService()后,只有
onDestroy()被回调标志着Service已停止。
相关文章推荐
- Android Service最全面的解析
- Android Service最全面的解析
- Android多线程全面解析:IntentService用法&源码
- Android四大组件:Service服务史上最全面解析
- Android Service最全面的解析
- Android IntentService使用全面介绍及源码解析
- Android:Service生命周期最全面解析
- 【转载】Android IntentService使用全面介绍及源码解析
- Android Service全面解析
- android四大组件-service全面解析一
- 关于Android Service的全面解析
- Android IntentService使用全面介绍及源码解析
- Android Service最全面的解析
- Android多线程全面解析:IntentService用法&源码
- Android Service非常全面的解析
- Android Service生命周期,本地服务远程服务,前台服务后台服务,启动方式,与IntentService区别全面解析
- Android AndroidManifest.xml文件属性全面解析
- Android Service 完全解析(上)
- Android IntentService解析
- Android 中的 Service 全面总结