关于Service服务常用的知识

一、Service的简单概述

在后台长时间运行操作而没有用户界面的应用组件,服务可由其他组件启动,即使用户切换到了其他应用,服务仍将继续在后台运行。例如,服务可以处理网络事物,播放音乐,执行文件I/O,与内容提供程序交互等等。

  • 服务有两种启动方式:
    (1)一种是服务可由其他应用组件启动(Activity),服务一旦被启动将在后台一直运行,即使启动服务的组件(Activity)已销毁也不受影响,一般情况下这种服务执行单一任务,如网络下载或者文件上传,一旦任务执行完毕,服务会自动停止。
    (2)另一种是组件可以绑定到服务,相当于提供了一个客户端-服务器的接口,允许组件和服务进行交互,发送请求,获取结果,甚至是执行进程间通信(IPC),服务可以同时绑定多个组件,当所有的组件都解绑时,服务才会自行销毁。
    (3)当然,服务可同时是这两种启动模式。
  • 服务在其托管的主线程中运行,它既不创建线程,也不在单独的进程中运行;因此如果在服务中执行CPU密集型工作或者阻止性工作,需要创建单独的线程来运行这部分代码,否则极容易会出现ANR情况。
二、Service服务在清单文件中的声明

无论是启动状态还是绑定状态,都需要通过继承Service基类自定义而来,也都需要在AndroidMainfest.xml中声明。

<Service 
android:enable=["true" | "false"] // 
android:exported=["true" | "false"] // 代表是否能被其他应用隐式调用
android:isolatedProcess=["true" | "false"] // 设置true意味着服务会在一个特殊的进程下运行,这个进程和系统其他进程分开且没有自己的权限。与其通信的唯一途径是通过服务API(bind & start)
android:label="string resource" // 
android:name="string" // 对应Service类名
android:permission="string" // 权限声明
android:process="string" // 是否需要在单独的进程中运行
</Service>
  • 注:android:process=":remote"时,代表Service在单独的进程中运行;“:”这个冒号很重要,它表示要在当前进程名称前面附加上当前的包名,所以“remote”和“:remote”不是同一个意思,前者的进程名称为:remote,后者的进程名称为:App-packageName:remote
三、Service中应重写的重要方法简述
  • onCreate():首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用onStartCommand()或onBind()方法之前),如果服务已经在运行,则不会调用。
  • onStartCommand():当一个组件调用startService()请求启动服务时,系统将调用此方法,即会启动服务并在后台无限期运行。如果使用此方法开启任务,则在服务完成后需要手动调用stopSelf()【Service自己调用该方法】或stopService()【由其他组件调用该方法】来停止服务。
  • onBind():当一个组件通过调用bindService()与服务绑定时,系统将会调用此方法;此方法实现中必须通过返回IBinder提供一个接口,供客户端用来与服务进行通信。
  • onDestroy():当服务不在使用且将被销毁时,系统将会调用这个方法;服务应该在此方法中来清理所有资源,如线程,注册的监听器,接收器等等。
四、从以下几点讲解服务的创建
1、因为在某些情况下系统会终止服务,因此从以下几点来学习服务的具体知识:
  • 如果将服务绑定到拥有用户焦点的Activity上时,服务优先级很高,不太可能被系统销毁。
  • 如果将服务声明为在前台运行,则它永远不会被终止。
  • 如果服务已开启并且需要长时间在后台运行,系统会随着时间的推移降低服务在后台任务列表中的位置,而服务也将随之变得非常容易被终止。
  • 如果是启动服务,就需要考虑被系统销毁后一旦有资源可以重启服务的情况
2、API的选择
  • Service:适用于所有服务的基类。扩展此类时,必须创建一个用于执行所有服务工作的新线程,因为默认情况下,服务将使用主线程。
  • IntentService:官方提供的Service的子类,它使用工作线程逐一处理所有启动请求。如果不需要服务同时处理多个请求,这是最好的选择。只需要实现onHandleIntent()方法即可,该方法会接收每个启动请求的Intent,依次执行这些请求。
  • startForeground():在前台运行服务
五、 启动方式:
1、onStartCommand(Intent intent, int flags, int startId)参数讲解

(1)intent:启动时,启动组件传递过来的Intent,如Activity可利用Intent封装所需要的参数并传递给Service;
(2)flags:表示启动请求时是否有额外数据,可选值有0,START_FLAG_REDELIVERY,START_FLAG_RETRY;

  • 0:代表没有
  • START_FLAG_REDELIVERY:这个值代表了onStartCommand方法的返回值为START_REDELIVER_INTENT,而且在上一次服务被杀死前会去调用stopSelf方法停止服务。其中START_REDELIVER_INTENT意味着当前Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个Intent调用onStartCommand(),此时Intent是有值的。
  • START_FLAG_RETRY:当onStartCommand调用后一直没有返回值时,会尝试重新去调用onStartCommand()。
    (3)startId:指明当前服务的唯一ID,与stopSelfResult(int startId)配合使用,stopSelfResult可以更安全的根据ID停止服务。
2、onStartCommand的返回值讲解(很重要)
  • START_STICKY:
    服务因为内存不足而被系统kill后,当内存再次空闲的时候会重新创建服务。一旦创建成功将会调用onStartCommand方法,不会重新传递最后一个Intent,此时传递的Intent为null。除非有挂起的Intent要启动服务;这个状态下比较适合不执行命令但无限期运行下去并等待作业的媒体播放器或类似服务。
  • START_NOT_STICKY:
    服务因内存不足而被系统kill后,除非有挂起的Intent要传递,否则不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。
  • START_REDELIVER_INTENT:
    服务因为内存不足被系统kill后,会重建服务,并通过传递给服务的最后一个intent调用onStartCommand,任何挂起intent均依次传递,与START_STICKY不同的是,其中传递的intent将是非空,是最后一次调用startService中的intent,这个情况适用于主动执行应该立即恢复的作业(如下载文件)的服务。
  • 总结:每次启动服务(调用startService)时,onStartCommand方法都会被调用,因此我们可以通过该方法使用intent给Service传递所需要的参数,然后在onStartCommand方法中处理事件,最后根据需求选择不同的Flag返回值。
  • 注:这种模式下,启动服务时传递的Intent参数是组件与服务之间唯一的通信模式,如果希望组件得到服务所执行任务的结果,可通过广播的形式建立连接。
  • 必须自己管理生命周期,建议服务自身调用stopSelf(int)方法,确保在多次启动服务的情况下正确停止服务。
六、Service绑定服务
1、关联:

组件可以向服务发送请求,或者调用服务的方法,此时被绑定的服务会接收信息并响应,甚至可以通过绑定服务执行进程间通信。
生命周期:调用bindService()时创建,调用unbindService()关闭连接;只在为其他组件服务时处于活动状态,不会无限期在后台运行;所有宿主解绑后,服务就会被系统销毁。

2、绑定到已启动服务:

可以创建同时具有已启动和绑定两种状态的服务;即通过startService()启动该服务,让服务无限期运行下去,还可以通过bindService()使客户端绑定到服务;同时具备两种状态的服务,系统不会在所有客户端都解除绑定之后销毁服务,而是通过调用stopSelf()/stopService()显示停止服务。

3、创建绑定服务,通过三种方法定义接口
  • 扩展Binder类:服务供自有应用专用,并且运行在与客户端相同的进程中,此时使用该方法;不以这种方式创建接口的唯一原因是服务被其他应用或不同的进程占用。
  • 使用Messager:需要让接口跨不同的进程工作,使用此方法;服务可用这种方式定义对应不同类型Message对象的Handler。此Handler是Messager的基础,后者随后可与客户端分享一个IBindler,从而让客户端能利用Message对象向服务发送命令;此外,客户端还可以定义自由Messenger,以便服务回传消息。这是执行进程间通信(IPC)的最简单方法,因为Messenger会在单一线程中创建包含所有请求的队列。
  • 使用AIDL(Android接口定义语言):执行所有将对象分解成原语的工作,操作系统可以识别这些原语并将它们编组到各进程中,以执行IPC;Messenger方法是建立在AIDL的基础之上的,Messenger会在单一线程中创建包含所有客户端请求的队列,此时服务一次接受一个请求;而AIDL可同时发送多个请求,此时服务就必须具备多线程处理的能力。
  • 绑定过程:因为服务绑定过程是异步的,因此要创建一个ServiceConnection实例。
  • 附加说明:如果只需要在Activity可见时与服务交互,则应该在onStart()期间绑定,在onStop()期间取消绑定。如果希望Activity在后台停止运行状态下仍可接收响应,则可在onCreate()期间绑定,在onDestroy()期间取消绑定。
七、在前台运行服务
  • 定义:
    前台服务被认为是用户主动意识到的一种服务,因此内存不足的时候系统也不会考虑销毁它。
  • 前台服务必须为状态栏提供通知,放在“正在进行”标题下方,这意味着除非服务停止或从前台移除,否则不能清除通知。
  • 请求让服务运行于前台:调用startForeground()
  • 从前台移除服务:调用stopForeground()
参考文章
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值