前言
在整理Android蓝牙开发的过程中,会用到后台播放音乐的功能,需要与service进行交互,因此对service作一个笔记,对以前未注意的细节作一次梳理。
一、service的基础知识
1.1 service的两种类型
-
started
当应用组件,如Activity通过startService()启动service,此时service处于started状态。一旦启动,此时service能够在后台一直运行,即使启动它的组件已经被销毁。一般,启动服务执行单个操作,并且不会向调用者返回结果。当然这种情况视需求而定。 -
Bound
当应用程序通过bindService()方法绑定到service时,service处于绑定状态。绑定服务可以为客户端-服务器提供接口,允许组件与service进行交互,发送请求,获得结果等操作,甚至还可以进行IPC(进程间通信)。仅当其他应用程序的组件与service绑定后,绑定服务才会允许。多个组件可以绑定到一个service上,并且当他们全部解除绑定时,service才可以被销毁。
1.2 service的线程
service运行于管理它的进程的主线程,即在APP内,service的线程id和主线程的id一致。可以使用log进行验证分别在MainActivity中的onCreate()中,以及自定义的MyService中的onCreate()中,执行Log
Log.e("TAG","主线程id = " + Thread.currentThread().getId());
Log.e(TAG,"Service的线程id = " + Thread.currentThread().getId());
得到的结果如下
06-21 03:49:28.491 5057-5057/com.example.slide_table E/MyService:主线程id=1
06-21 03:49:28.495 5057-5057/com.example.slide_table E/MyService:Service的线程id=1
如果需要在service中运行比较耗时的操作,可以在service中创建新的线程来完成这些操作,以减少应用可能出现的ANR。
二、service中的重要方法
在实际研发的过程中,几乎都需要对service中的回调方法进行重写以期达到需求。经常需要重写的几种回调方法如:
onCreate()
服务第一次创建时,会执行这个函数,创一次性创建service,如果service已经存在,则不需要再次执行。onStartCommand()
当其他组件,例如activity调用startService()的方法请求启动service时,系统调用该方法,一旦执行此方法,service会一直运行于后台。同理,在服务完成后,如果需要停止service,可以调用stopSelf()或者stopService()方法停止service,如果用户只是想绑定service,则不需要重写该方法。onBind()
当其他的组件通过bindService()方法与service进行绑定,系统会调用该方法,但是在开发中必须通过返回IBinder提供客户端与service通信的接口,如果用户不想进行绑定,则返回null。onDestroyed()
当不需要service,需要对service进行销毁的时候调用该方法,服务应该利用该方法来实现清理线程,注册监听器等资源,这也是service收到的最后一个方法调用。需要注意的是用户是通过startService()启动该service时(此时service会调用onStartCommand()),服务调用stopSelf()方法停止自身。或者其他的组件使用stopService()来停止该service。
当用户的系统内存不足时,必须回收系统资源来显示用户关注的activity时,才会停止service的运行。但是若此service为用户关注的activity所绑定的,则会减少回收的几率。如果服务被声明运行于前台,则基本不会停止。
如果service处于started状态,虽然会一直运行于后台,但是为了降低这种系统资源被回收的风险,需要对重启该service进行设计。当系统资源变得充足可用时,可重新启动该service,但是依赖于onStartCommand()的返回值。
三、service配置的几种属性
当在开发中声明某service时,必须在AndroidManifest.xml中对该服务进行配置。常用的几种属性为:
android:name
实现该service的名称,一个完整的类名,通常简写为".MyService"。android:enabled
服务能否被系统实例化,可以则返回true,or false,默认为true。android:exported
其他组件能否调用服务或者与其交互,可以则返回true,or false。当表示false时,只有该APP内的应用组件或者与APP具有相同用户ID的APP能启动或者与之绑定。android:label
显示给用户的service名称,如果没有设置,以应用程序APP的标签取代。android:permission
实体必须包含的权限名称,如果没有设置该属性,可以使用<application>标签的permission属性设置服务权限。
四、service的创建
通过继承创建service,Android提供以下两个类供开发继承:
4.1 Service
所有service的基类,当继承该类时,创建新的线程来处理需要执行的动作,避免出现ANR的情况。特点:
- 可以实现服务处理多线程的任务
- 每次请求创建一个新线程并且立即执行,不用等待前一个请求的结束
4.2 IntentService
该类为service的子类,每次都会使用一个工作线程来处理全部的启动请求。在APP中如果不是处理多个请求,最优的创建服务继承该类。开发人员只需要重写onHandleIntent()方法,来执行接收到的intent,从而完成后台服务。
特点:
- 创建工作线程来执行发送到onStartCommand()的全部intent
- 创建工作队列,一次传递一个Intent给onHandleIntent()
- 避免出现多线程
- 所有启动service请求完毕后,自动停止service,不需要调用stopSelf()停止该service
- 提供的onBind()方法默认实现,返回null
- 提供onStartCommand()方法默认实现,先将intent发送给工作队列,然后在发送给onHandleIntent()执行
- 只需要在onHandleIntent()执行客户端提供的任务
注意:IntentService并没有提供构造函数,因此在继承IntentService后,需提供一个无参的构造方法。
public class MyIntentService extend IntentService{
public MyIntentService(){
super("MyIntentService ");
}
}
五、service中的重要函数
onStartCommand()
在组件请求启动service后,会发送Intent,服务开启后,调用onStartCommand()方法执行该任务。
但是该方法必须返回一个整数,用于描述系统停止service后如何继续服务,此点仅在继承自service的服务中使用,因为继承IntentService已经默认实现。返回值:
-
START_NOT_STICKY
在执行onStartCommand()完成任务后,停止该service,不重新创建服务,则返回 -
START_STICKY
在执行onStartCommand()完成任务后,停止该service,重新创建服务,并且再次调用onStartCommand(),但是不重新发送最后的Intent。相反,系统使用空Intent调用onStartCommand(),除非有PendingIntent来启动该service,此时这些Intent会被发送。
此返回值经常用于多媒体播放器,它们不执行命令,但无限期的运行并等待命令。 -
START_REDELIVER_INTENT
在执行onStartCommand()返回后停止该service,重新创建服务,并使用发送给服务最后的Intent重新调用onStartCommand(),全部的PendingIntent依次发送。此返回值适用于积极执行并且立即恢复工作的服务,如wifi模式下后台下载文件,断开wifi后,文件下载中断,重新打开wifi,继续下载文件。
六、service的启动与停止
6.1 service的启动
service的启动分为传递intent启动以及绑定启动,本章主要讲述通过传递intent来启动service。
通过activity或者其他应用组件来传递Intent来启动service,系统调用service的onStartCommand()并且将该Intent传递给它。
Intent service_intent = new Intent(MAcitivty.class, MyService.class);
startService(service_intent);
/**
如果activity与fragment交互,需要使用
getActivity().startService(service_intent);
*/
如果service没有提供绑定,则startService()发送的intent是APP的组件与该service唯一的通信模式。如果需要服务返回结果,则启动该服务的客户端能为广播创建一个PendingIntent,并且通过启动service的Intent进行传送。服务便可以通过广播来发送结果。
6.2 service的停止
通常情况下,APP在服务完成任务后,应该停止服务,避免造成系统资源的浪费和电池的消耗。系统不会自动的停止或者销毁服务,除非系统资源紧缺需要系统资源的回收,而且还可以在onStartCommand()返回后,服务继续运行。service本身可以调用stopSelf()停止自身,或者其他的应用组件调用stopService()来停止该service。
注意,如果该service同时处理多个onStartCommand()的调用请求,在处理一个请求后,首先需要判断另一个新的请求的ID是否与上一个请求的ID是否相同,即在使用stopSelf(int)方法停止该服务时,同时将新的启动service的请求ID传递给停止请求进行匹配,如果下一个新的请求的ID与上一个请求的ID不相同,则不会停止本次的服务,这样就会避免多次启动服务的请求,而多次调用onStartCommand()。