一、What is a service ?
Service作为Android的四大组件之一,往往执行一些后台的耗时操作,比如网络下载上传、媒体播放、IO操作、与content provider交互等等。Service可以分为两大类,其一:开启服务之后即自己运行自己的,往往没有返回结果,不需要与activity交互,这类服务叫做started;其二:需要与activity交互,形成一个C/S的交互模式,比如更新界面等等,这类服务叫做bound。
二、How to create it ?
与其它四大组件类似,往往需要继承Service类(或者某些Service的子类,比如IntentService),并重写某些生命周期方法。其中几个比较重要的生命周期方法如下:
1.onCreate:往往进行一次性配置工作,比如创建子线程(因为service默认运行在主线程中,如果执行某些耗时的操作,会出现ANR),此方法只会执行一次。
2.onStartCommand:此方法会在activity调用startService之后执行,bindService不会执行此方法,主要进行一些主要的逻辑的处理,执行完成后往往需要进行stopSelf结束服务。为了方便操作,我们往往使用IntentService。
3.onBind:服务的开启方式为bindService时,会执行此方法。此时返回一个binder对象(继承自Binder,在service中采用内部类的方式实现,与activity交互的代理),然后在activity中实现ServiceConnection接口(往往采用内部类),并重写其中onServiceConnected和onServiceDisconnected方法,获取binder对象,进行交互。
4.常见的创建方式
<span style="font-size:14px;">public class HelloIntentService extends IntentService {
/**
* 构造方法是必需的,必须用工作线程名称作为参数
* 调用父类的[http://developer.android.com/reference/android/app/IntentService.html#IntentService(java.lang.String) IntentService(String)]构造方法。
*/
public HelloIntentService() {
super("HelloIntentService");
}
/**
* IntentService从缺省的工作线程中调用本方法,并用启动服务的intent作为参数。
* 本方法返回后,IntentService将适时终止这个服务。
*/
@Override
protected void onHandleIntent(Intent intent) {
// 通常我们会在这里执行一些工作,比如下载文件。
// 作为例子,我们只是睡5秒钟。
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
}</span>
<span style="font-size:14px;">public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// 处理从线程接收的消息
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// 通常我们在这里执行一些工作,比如下载文件。
// 作为例子,我们只是睡个5秒钟。
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
// 根据startId终止服务,这样我们就不会在处理其它工作的过程中再来终止服务
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
// 启动运行服务的线程。
// 请记住我们要创建一个单独的线程,因为服务通常运行于进程的主线程中,可我们不想阻塞主线程。
// 我们还要赋予它后台运行的优先级,以便计算密集的工作不会干扰我们的UI。
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// 获得HandlerThread的Looper队列并用于Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// 对于每一个启动请求,都发送一个消息来启动一个处理
// 同时传入启动ID,以便任务完成后我们知道该终止哪一个请求。
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// 如果我们被杀死了,那从这里返回之后被重启
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
// 我们不支持绑定,所以返回null
return null;
}
@Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}</span>
三、lifecycle
四、其他
1、关于onStartCommand的返回值
-
START_NOT_STICKY
-
如果系统在onStartCommand()返回后杀死了服务,则不会重建服务了,除非还存在未发送的intent。 当服务不再是必需的,并且应用程序能够简单地重启那些未完成的工作时,这是避免服务运行的最安全的选项。
-
START_STICKY
-
如果系统在onStartCommand()返回后杀死了服务,则将重建服务并调用onStartCommand(),但不会再次送入上一个intent, 而是用null intent来调用onStartCommand() 。除非还有启动服务的intent未发送完,那么这些剩下的intent会继续发送。 这适用于媒体播放器(或类似服务),它们不执行命令,但需要一直运行并随时待命。
-
START_REDELIVER_INTENT
-
如果系统在onStartCommand()返回后杀死了服务,则将重建服务并用上一个已送过的intent调用onStartCommand()。任何未发送完的intent也都会依次送入。这适用于那些需要立即恢复工作的活跃服务,比如下载文件。
2、有一件事我们需要明确,那就是每一个服务只会有一个实例,比如我有如下执行过程startService、startService、stopService、stopService,分析如下,第一次调用startService会有oncreate-->onStartCommand,第二次调用startService则只会执行onStartCommand,接下来调用stopService,则服务销毁,第二次执行stopService不会起作用,但也不会出现异常(服务只会调用一次onCreate)。
3.startService和bindService是可以同时时候用的,比如有如下执行过程startService、bindService,则需要注意的是在销毁服务的时候需要依次执行unbindService,stopService。例如,一个后台音乐服务可以通过调用startService()来启动,传入一个指明所需播放音乐的 Intent。 之后,用户也许需要用播放器进行一些控制,或者需要查看当前歌曲的信息,这时一个activity可以通过调用bindService()与此服务绑定。在类似这种情况下,stopService()或stopSelf()不会真的终止服务,除非所有的客户端都解除了绑定。
4.前台服务,通过使用startForeground+Notification类可以参考http://blog.csdn.net/yyingwei/article/details/8509402
5.使用Messenger和AIDL进行IPC
6.参考文献
官方文档