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。
//Client
//start service
Intent intent = new Intent(ServiceStartActivity.this,
HelloIntentService.class);
intent.putExtra("value", "From ServiceStartActivity");
startService(intent);
//stop service
Intent intent = new Intent(ServiceStartActivity.this,
HelloIntentService.class);
stopService(intent);
//Service
public class HelloIntentService extends IntentService {
@Override
protected void onHandleIntent(Intent intent) {
logger.i("onHandleIntent " + intent.getStringExtra("value"));
long endTime = System.currentTimeMillis() + 5 * 1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
logger.e(e.getMessage());
}
}
}
}
}
如果你还是想让Service来执行多线程任务,那么你需要继承Service处理每一个intent。
官网上的例子还是一次只能执行一个请求,请求都在一个线程中等待上一个请求结束,我修改了下,让请求可以同时进行,所有请求处理结束后,销毁Service。
//Client //start service Intent intent = new Intent(ServiceStartActivity.this,HelloService.class); startService(intent); //stop service Intent intent = new Intent(ServiceStartActivity.this,HelloService.class); stopService(intent); //Service //用来处理接受的请求 public class HelloService extends Service { private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { logger.i("handleMessage1"); long endTime = System.currentTimeMillis() + 5 * 1000; while (System.currentTimeMillis() < endTime) { synchronized (this) { try { wait(endTime - System.currentTimeMillis()); } catch (InterruptedException e) { // TODO Auto-generated catch block logger.e(e.getMessage()); } } } logger.i("stopSelf " + msg.arg1); stopSelf(msg.arg1); // stopSelfResult(msg.arg1); } } @Override public int onStartCommand(Intent intent, int flags, int startId) { logger.i("onStartCommand " + startId); //为每一次的请求分配线程 HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); Looper looper = thread.getLooper(); Handler handler = new ServiceHandler(looper); Message msg1 = handler.obtainMessage(); msg1.arg1 = startId; handler.sendMessage(msg1); return START_STICKY; } }
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,那么通知也会从状态栏移除。看下实例代码。
//Client //start service Intent intent = new Intent(ServiceStartActivity.this,LocalService.class); intent.putExtra("value", "1234"); startService(intent); //stop service Intent intent = new Intent(ServiceStartActivity.this,LocalService.class); stopService(intent); //Service public class LocalService extends Service { private NotificationManager mNm; private static final Class[] mStartForegroundSignature = new Class[] { int.class, Notification.class }; private static final Class[] mStopForegroundSignature = new Class[] { boolean.class }; private Method mStartForeground; private Method mStopForeground; private Object[] mStartForegroundArgs = new Object[2]; private Object[] mStopForegroundArgs = new Object[1]; //开启Foreground Service,为了兼容早期版本,2.0之前的版本调用setForeground()方法 void startForegroundCompat(int id, Notification notification) { if (mStartForeground != null) { mStartForegroundArgs[0] = Integer.valueOf(id); mStartForegroundArgs[1] = notification; try { mStartForeground.invoke(this, mStartForegroundArgs); } catch (InvocationTargetException e) { logger.e(e.getMessage()); } catch (IllegalAccessException e) { logger.e(e.getMessage()); } return; } setForeground(true); mNm.notify(id, notification); } //移除Foreground Service void stopForegroundCompat(int id) { if (mStopForeground != null) { mStopForegroundArgs[0] = Boolean.TRUE; try { mStopForeground.invoke(this, mStopForegroundArgs); } catch (InvocationTargetException e) { logger.e(e.getMessage()); } catch (IllegalAccessException e) { logger.e(e.getMessage()); } return; } mNm.cancel(id); setForeground(false); } @Override public void onCreate() { super.onCreate(); logger.w("onCreate threadID" + Thread.currentThread().getId()); mNm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); try { mStartForeground = getClass().getMethod("startForeground", mStartForegroundSignature); mStopForeground = getClass().getMethod("stopForeground", mStopForegroundSignature); } catch (NoSuchMethodException e) { mStartForeground = mStopForeground = null; } showBroadCast(); } private void showBroadCast() { Notification notification = new Notification(R.drawable.btn_star, "afasdf", System.currentTimeMillis()); Intent intent = new Intent(this, ServiceStartActivity.class); PendingIntent pi = PendingIntent.getBroadcast(this, 123, intent, 0); notification.setLatestEventInfo(this, "AAA", "BBB", pi); startForegroundCompat(R.string.dialog_alert_title, notification); } }
需要注意的是在android2.0之后引入 startForeground()和
stopForeground()方法,如果你的程序想在android2.0之前运行Foreground Service,你必须使用之前的方法
setForeground() 。