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
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来执行多线程任务,那么你需要继承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() 。