Service是一种可以在后台执行长时间任务的没有交互界面的应用组件,Service运行在进程中的主线程中,它不能自己建立线程处理长时间的事务,所以需要你手动为你的Service创建独立的线程来避免系统没有响应(ANR)等问题。
Service生命周期
1. startService
startService()->onCreate()->onStartCommand()->running->onDestory()->shut down
多次调用startService()方法会多次执行Service的onStartCommand()方法,然后只需调用一次stopService()去停止Service。
当Service启动后,独立运行在后台,即使应用组件被Destory了,Service也不会停止,Service应该应该在完成事务后调用stopSelf(),或者在其他应用组件中调用stopService()。
2. bindService
bindService()->onCreate()->onBind()->客户端绑定Service->onUnBind()->onDestory()->shut down
多个应用组件可以绑定同一个Service,当客户端和Service绑定后,调用unbindService()来进行解绑,直到所有调用该Service的客户端都解绑了,系统才会去销毁Service。
这类Service不会独立运行在后台(设置->运行的服务中看到不你的Service),它的生命周期与绑定它的应用组件相关,当应用组件销毁了,Service会调用onDestory。
如果在一个Activity中多次绑定Service,在解绑的时候需要多次调用unbindService(),但此时系统会抛出异常:
java.lang.IllegalArgumentException: Service not registered:
所以按照官方的要求(忘记在哪看到了,找不到了,应该就在官网上有说明),需要在绑定和解绑过程中做一个标志位,当Service已经解绑了,才能再次被绑定。官网的实例代码也是这么做的。
处理多个请求
IntentService继承Service,使用工作队列来处理所有请求,一次处理一件。当有多个请求同时发出,可以保证线程的安全性。你所要做的只需要实现onHandleIntent()方法在后台处理从不同的Intent发来的请求。
下面写了一个小实例,发送3个请求,每隔5秒钟打印信息,并且在所有请求完成后销毁Service。
03 | Intent intent = new Intent(ServiceStartActivity. this , |
04 | HelloIntentService. class ); |
05 | intent.putExtra( "value" , "From ServiceStartActivity" ); |
09 | Intent intent = new Intent(ServiceStartActivity. this , |
10 | HelloIntentService. class ); |
14 | public class HelloIntentService extends <span style= "color:#e53333;" >IntentService </span>{ |
17 | protected void onHandleIntent(Intent intent) { |
19 | logger.i( "onHandleIntent " + intent.getStringExtra( "value" )); |
21 | long endTime = System.currentTimeMillis() + 5 * 1000 ; |
22 | while (System.currentTimeMillis() < endTime) { |
25 | wait(endTime - System.currentTimeMillis()); |
26 | } catch (Exception e) { |
27 | logger.e(e.getMessage()); |
![](http://image60.360doc.com/DownloadImg/2013/04/1013/31542102_4.jpg)
如果你还是想让Service来执行多线程任务,那么你需要继承Service处理每一个intent。
官网上的例子还是一次只能执行一个请求,请求都在一个线程中等待上一个请求结束,我修改了下,让请求可以同时进行,所有请求处理结束后,销毁Service。
03 | Intent intent = new Intent(ServiceStartActivity. this ,HelloService. class ); |
07 | Intent intent = new Intent(ServiceStartActivity. this ,HelloService. class ); |
12 | public class HelloService extends Service { |
13 | private final class ServiceHandler extends Handler { |
14 | public ServiceHandler(Looper looper) { |
19 | public void handleMessage(Message msg) { |
20 | logger.i( "handleMessage1" ); |
21 | long endTime = System.currentTimeMillis() + 5 * 1000 ; |
22 | while (System.currentTimeMillis() < endTime) { |
25 | wait(endTime - System.currentTimeMillis()); |
26 | } catch (InterruptedException e) { |
27 | // TODO Auto-generated catch block |
28 | logger.e(e.getMessage()); |
32 | logger.i( "stopSelf " + msg.arg1); |
34 | // stopSelfResult(msg.arg1); |
40 | public int onStartCommand(Intent intent, int flags, int startId) { |
41 | logger.i( "onStartCommand " + startId); |
44 | HandlerThread thread = new HandlerThread( "ServiceStartArguments" , |
45 | Process.THREAD_PRIORITY_BACKGROUND); |
48 | Looper looper = thread.getLooper(); |
50 | Handler handler = new ServiceHandler(looper); |
51 | Message msg1 = handler.obtainMessage(); |
53 | handler.sendMessage(msg1); |
![](http://image60.360doc.com/DownloadImg/2013/04/1013/31542102_5.jpg)
onStartCommand()的返回值用来描述,系统将如何处理被kill的Service。
START_NOT_STICKY
如果系统在onStartCommand()返回之后杀掉Service,不会重建Service,除非一个intent被传递给Service。可以避免你在必须要的时候运行你的Service。
START_STICKY
如果系统在onStartCommand()返回之后杀掉Service,重建Service并调用onStartCommand(),但不会传递最后一次Intent,换句话说,onStartCommand()所接受到的Intent是null值,除非有一个intent启动了Service。
START_REDELIVER_INTENT
如果系统在onStartCommand()返回之后杀掉Service,重建Service并调用onStartCommand(),并传递最后一次Intent给Service。
运行一个Foreground Service
一个Foreground Service即便在内存较低的情况下也不会被系统kill掉。Foreground Service提供了一个通知在状态栏,这个通知在“ongoing”栏中,意思是通知不会消失除非Service停止或者从Foreground移除。调用startForeground()启动foreground service,从前台移除Service,调用stopForeground()方法,这个方法不会让Service停止,如果停止了Service,那么通知也会从状态栏移除。看下实例代码。
03 | Intent intent = new Intent(ServiceStartActivity. this ,LocalService. class ); |
04 | intent.putExtra( "value" , "1234" ); |
08 | Intent intent = new Intent(ServiceStartActivity. this ,LocalService. class ); |
12 | public class LocalService extends Service { |
14 | private NotificationManager mNm; |
16 | private static final Class[] mStartForegroundSignature = new Class[] { |
17 | int . class , Notification. class }; |
18 | private static final Class[] mStopForegroundSignature = new Class[] { boolean . class }; |
20 | private Method mStartForeground; |
21 | private Method mStopForeground; |
22 | private Object[] mStartForegroundArgs = new Object[ 2 ]; |
23 | private Object[] mStopForegroundArgs = new Object[ 1 ]; |
25 | //开启Foreground Service,为了兼容早期版本,2.0之前的版本调用setForeground()方法 |
26 | void startForegroundCompat( int id, Notification notification) { |
28 | if (mStartForeground != null ) { |
29 | mStartForegroundArgs[ 0 ] = Integer.valueOf(id); |
30 | mStartForegroundArgs[ 1 ] = notification; |
32 | mStartForeground.invoke( this , mStartForegroundArgs); |
33 | } catch (InvocationTargetException e) { |
35 | logger.e(e.getMessage()); |
36 | } catch (IllegalAccessException e) { |
38 | logger.e(e.getMessage()); |
43 | mNm.notify(id, notification); |
46 | //移除Foreground Service |
47 | void stopForegroundCompat( int id) { |
49 | if (mStopForeground != null ) { |
50 | mStopForegroundArgs[ 0 ] = Boolean.TRUE; |
52 | mStopForeground.invoke( this , mStopForegroundArgs); |
53 | } catch (InvocationTargetException e) { |
55 | logger.e(e.getMessage()); |
56 | } catch (IllegalAccessException e) { |
58 | logger.e(e.getMessage()); |
68 | public void onCreate() { |
71 | logger.w( "onCreate threadID" + Thread.currentThread().getId()); |
73 | mNm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); |
76 | mStartForeground = getClass().getMethod( "startForeground" , |
77 | mStartForegroundSignature); |
78 | mStopForeground = getClass().getMethod( "stopForeground" , |
79 | mStopForegroundSignature); |
80 | } catch (NoSuchMethodException e) { |
82 | mStartForeground = mStopForeground = null ; |
88 | private void showBroadCast() { |
89 | Notification notification = new Notification(R.drawable.btn_star, |
90 | "afasdf" , System.currentTimeMillis()); |
91 | Intent intent = new Intent( this , ServiceStartActivity. class ); |
92 | PendingIntent pi = PendingIntent.getBroadcast( this , 123 , intent, 0 ); |
93 | notification.setLatestEventInfo( this , "AAA" , "BBB" , pi); |
95 | startForegroundCompat(R.string.dialog_alert_title, notification); |
需要注意的是在android2.0之后引入
startForeground()和
stopForeground()方法,如果你的程序想在android2.0之前运行Foreground Service,你必须使用之前的方法
setForeground() 。