Service(服务)是能够在后台执行长时间运行操作并且不提供用户界面的应用程序组件。其他应用程序组件能启动服务,并且即便用户切换到另一个应用程序,服务还可以在后台运行。此外,组件能够绑定到服务并与之交互,甚至执行进程间通信(IPC).
一、Service概述
1.Service的分类
服务从 本质上可以分为以下两种类型
①Started(启动):
当应用程序组件(如activity)通过调用startService()方法启动服务时,服务处于started状态。一旦启动,服务能在后台无限期运行,即使启动它的组件已经被销毁。通常,启动服务执行单个操作并且不会向调用者返回结果。例如,它可能通过网络下载或者上传文件。如果操作完成,服务需要停止自身。
②Bound(绑定):
当应用程序组件通过调用bindService()方法绑定到服务时,服务处于bound状态。绑定服务提供客户端-服务器接口,以允许组件与服务交互、发送请求、获得结果,甚至使用进程间通信(IPC)跨进程完成这些操作。仅当其他应用程序组件与之绑定时,绑定服务才运行。多个组件可以一次绑定到一个服务上,当它们都解绑定时,服务被销毁。
服务也可以同时属于这两种类型,既可以启动(无限期运行)也能绑定。其重点在于是否实现一些回调方法:onStartCommand()方法允许组件启动服务;onBind()方法允许组件绑定服务。
服务运行于管理它的进程的主线程,服务不会创建自己的线程,也不会运行于独立的进程(除非开发人员定义)。这意味着,如果服务要完成CPU密集工作或者阻塞操作,开发人员需要在服务中创建新线程来完成这些工作。通过使用独立的线程,能减少应用程序不响应(ANR)错误的风险,并且应用程序主线程仍然能用于用户与Activity的交互。
2.Service类中的重要方法
为了创建服务,开发人员需要创建Service类(或其子类)的子类。在实现类中,需要重写一些处理服务生命周期重要方面的回调方法,并根据需要提供组件绑定到服务的机制。需要重写的重要回调方法如下:
①onStartCommand()
当其他组件调用startService()方法请求服务启动时,系统调用该方法。一旦该方法执行,服务就启动并在后台无限期运行。如果开发人员实现该方法,则需要在任务完成时调用stopSelf()或stopService()方法停止服务(如果仅想提供绑定,则不必实现该方法)。
②onBind()
当其他组件调用bindService()方法想与服务绑定时(如执行RPC),系统调用该方法。在该方法的实现中,开发人员必须通过返回IBinder提供客户端用来与服务通信的接口。该方法必须实现,但是如果不想允许绑定,则返回null。
③onCreate()
当服务第一次创建时,系统调用该方法执行一次性建立过程(在系统调用onStartCommand()或onBind()方法前)。如果服务已经运行,该方法不被调用。
④onDestroy()
当服务不再使用并即将销毁时,系统调用该方法。服务应该实现该方法来清理诸如线程、注册监听器、接收者等资源。这是服务收到的最后调用。
如果组件调用startService()方法启动服务,服务需要使用stopSelf()方法停止自身,或者其他组件使用stopService()方法停止该服务。
如果组件调用bindService()方法创建,服务运行时间与组件绑定到服务的时间一样长。一旦服务从所有客户端解绑定,系统会将其销毁。
Android系统仅当内存不足并且必须回收系统资源来显示用户关注的activity时,才会强制停止服务。如果服务绑定到用户关注的activity,则会减少停止概率。如果服务被声明为前台运行,则基本不会停止。否则,如果服务是started状态并且长时间运行,则系统会随着时间推移降低其在后台任务列表中的位置并且有很大概率将其停止。如果服务是started状态,则必须设计系统重启服务。系统停止服务后,资源可用时会将其重启(但这也依赖于onStartCommand()方法的返回值)。
3.Service的声明
为了声明Service,需要向标签中添加子标签,子标签的语法如下:
android:enabled=["true"|"false"]
android:exported=["true"|false"]
android:icon="drawable resource"
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string"
...
>
各个标签属性的说明如下:
①android:enabled
服务能否被系统实例化,true表示可以,false表示不可以,默认值是true。标签也有自己的enabled属性,用于包括服务的全部应用程序组件。和的enabled属性必须同时设置成true(两者的默认值都是true)才能让服务可用。如果任何一个是false,服务被禁用并且不能实例化。
②android:exported
其他应用程序组件能否调用服务或者与其交互,true表示可以,false表示不可以。当该值是false时,只有同一个应用程序的组件或者具有相同用户ID的应用程序能启动或者绑定到服务。
默认值依赖于服务是否包含Intent过滤器。若没有过滤器,服务仅能能通过精确类名调用,这意味着服务仅用于应用程序内部(因为其他程序可能不知道类名)。此时,默认值是false;若存在至少一个过滤器,暗示服务可以用于外部,音质默认值是true。
该属性不是限制其他应用程序使用服务的唯一方式。还可以使用permission属性限制外部实体与服务交互。
③android:icon
表示服务的图标
④android:label
显示给用户的服务名称
⑤android:name
实现服务的Service子类名称,应该是一个完整的类名
⑥android:permission
实体必须包含的权限名称,以便启动或者绑定到服务。
如果startService()、bindService()或者stopService()方法调用者没有被授权,方法调用无效,并且Intent对象也不会发送给服务。
如果没有设置该属性,使用标签的permission属性设置给服务。如果和标签的permission属性都未设置,服务不受权限保护。
⑦android:process
服务运行的进程名称。
通常,应用程序的全部组件运行于为应用程序创建的默认进程。进程名称与应用程序包名相同。
标签的precess属性能为全部组件设置一个相同的默认值。但是组件能用自己的process属性重写默认值,从而允许应用程序跨越多个进程。
如果分配给该属性的名称以冒号(:)开头,仅属于应用程序的新进程会在需要时创建,服务能在该进程中运行;
如果进程名称以小写字母开头,服务会运行在以此为名的全局进程,但需要提供相应的权限。这允许不同应用程序组件共享进程,减少资源使用。