首页 > 代码库 > Develop系列-API Guides-应用组件-Services
Develop系列-API Guides-应用组件-Services
Services
Services:可在后台长时间运行,无UI界面。
Started:
Services通过其他组件调用startService启动,能在后台一直运行,即使启动它的组件已经销毁。一般这种服务执行特定的操作而且没有返回值,比如通过网络下载或上传一个文件,一旦操作结束,服务需要停止自己。
Bound:
Service通过其他组件调用bindService绑定,提供一个CS架构的接口允许其他组件与Service交互、发消息、获取返回值、甚至跨进程通信。bound service与绑定它的组件共存亡,多个组件可以绑定一个服务,当这个服务所有绑定它的组件都解绑后,服务会被销毁。
Caution:Service在宿主进程的主线程运行,也就是说Service不会创建自己的线程,也不会运行在另一个进程(除非你指定)。这就意味着,如果Service执行一些高CPU负载或阻塞界面操作的任务时,你需要创建新的线程来运行它,以降低ANR的风险。
(使用service还是thread?如果你只是需要在与用户交互时,在主线程之外执行额外的任务,那么你需要创建一个新线程而不是Service。比如在activity在前台显示的时候播放音乐,你需要在onCreate里面创建线程,在onStart执行线程,在onStop里停止线程,当然也可以使用AsyncTask或者HandleThread来替代线程类。)
基础
创建Service的子类,覆写Service生命周期中的关键回调函数,如果有必要,提供绑定Service的机制。最重要的回调函数如下:
onStartCommand
系统在其他组件调用startService启动Service时执行,之后,Service一直在后台执行。如果你实现了这个方法,那么你有责任在Service完成其工作时通过stopSelf或stopService来停止Service。(如果只是提供绑定Service,可以不用实现这个方法)
onBind
系统在其他组件调用bindService绑定Service时执行,在这个方法的实现中,你需要返回一个IBider接口用于clients与service交互。如果你不需要绑定,可以直接返回null
onCreate
Service第一次创建时执行,在onStartCommand和onBind之前,用于执行一次性需要初始化好的数据。
onDestroy
service销毁时执行,用于清理资源,类似线程、注册监听器,receiver等。
系统会在低内存时强制停止service。如果service绑定在一个有用户焦点的activity,那么它很少会被销毁;如果服务定义为在前台运行的,那么它永远不会被销毁。
在manifest定义service
<manifest ... > ... <application ... > <service android:name=".ExampleService" /> ... </application></manifest>
android:name是唯一必填的属性,其他可以选填。一旦你的应用发布出去了,那么android:name永远不要再变。
为了安全性,请使用显式Intent启动或绑定服务,而不是定义filiters。如果确实允许多个服务来响应,那么请务必在Intent中通过setPackage来指定报名,以降低目标服务匹配出意外值得概率。
设置android:exported为false,可以避免其他应用启动这个服务。
创建service
Service
所有服务的基类,如果你扩展这个类,那么你可能需要创建一个新的线程,因为这个服务室在app的主线程中执行,有可能会降低activity的流畅度
IntentService
Service 的子类,通过工作线程来处理所有请求,每次处理一个。如果你的service不需要同时处理多个请求,IntentService是最佳的选择。
扩展IntentService
因为大部分服务不需要同时处理多个请求(实际上是个多线程场景),IntentService是最好的推荐
IntentService工作流程如下:
- 创建一个默认的新工作线程来执行所有分发给onStartCommand的intents
- 创建一个工作队列来依次传递intent去执行onHandleIntent具体实现,所以不需要考虑多线程场景
- 所有请求执行完之后,自动销毁,你不需要执行stopSelf
- 提供默认的onBind实现:直接返回null
- 提供onStartCommand默认实现:发送intent给工作线程执行onHandleIntent实现
实际上,你需要做的就是实现onHandleIntent,如下例子:
public class HelloIntentService extends IntentService { /** * A constructor is required, and must call the super IntentService(String) * constructor with a name for the worker thread. */ public HelloIntentService() { super("HelloIntentService"); } /** * The IntentService calls this method from the default worker thread with * the intent that started the service. When this method returns, IntentService * stops the service, as appropriate. */ @Override protected void onHandleIntent(Intent intent) { // Normally we would do some work here, like download a file. // For our sample, we just sleep for 5 seconds. long endTime = System.currentTimeMillis() + 5*1000; while (System.currentTimeMillis() < endTime) { synchronized (this) { try { wait(endTime - System.currentTimeMillis()); } catch (Exception e) { } } } }}
如果你需要覆写其他回调,比如onCreate、onStartCommand、onDestroy,请确保要调用父类的实现,这样IntentService才能处理工作线程的生命周期。onHandleIntent和onBind是例外。
扩展Service
如果服务需要支持多线程(而不是通过工作队列来一一处理),那么你要扩展Service来处理每一个intent。
作为对比,如下代码和上面代码功能一样。
public class HelloService extends Service { private Looper mServiceLooper; private ServiceHandler mServiceHandler; // Handler that receives messages from the thread private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { // Normally we would do some work here, like download a file. // For our sample, we just sleep for 5 seconds. long endTime = System.currentTimeMillis() + 5*1000; while (System.currentTimeMillis() < endTime) { synchronized (this) { try { wait(endTime - System.currentTimeMillis()); } catch (Exception e) { } } } // Stop the service using the startId, so that we don‘t stop // the service in the middle of handling another job stopSelf(msg.arg1); } } @Override public void onCreate() { // Start up the thread running the service. Note that we create a // separate thread because the service normally runs in the process‘s // main thread, which we don‘t want to block. We also make it // background priority so CPU-intensive work will not disrupt our UI. HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); // Get the HandlerThread‘s Looper and use it for our 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(); // For each start request, send a message to start a job and deliver the // start ID so we know which request we‘re stopping when we finish the job Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; mServiceHandler.sendMessage(msg); // If we get killed, after returning from here, restart return START_STICKY; } @Override public IBinder onBind(Intent intent) { // We don‘t provide binding, so return null return null; } @Override public void onDestroy() { Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); }}
这个只是模拟IntentService处理,有必要的话,你可以为每一个请求创建一个线程。
onStartCommand必须返回一个整型,这个值用来描述系统如何处理这个服务,当它被异常销毁时。
START_NOT_STICKY
如果服务执行完onStartCommand后被异常销毁,不需要重新创建服务,除非还有未处理的intents。避免服务被不必要地启动,你的应用能够很容易地重新启动未完成的工作,选择这个。
START_STICKY
如果服务执行完onStartCommand后被异常销毁,重新创建服务并执行onStartCommand,但是不会使用发给这个服务的最后一个intent,而是通过一个null intent来执行onStartCommand,除非还有未处理的intents。这种情况比较适合不执行命令,一直在后台运行或者等待任务的媒体播放。
START_REDELIVER_INTENT
如果服务执行完onStartCommand后被异常销毁,重新创建服务,用发给这个服务的最后一个intent调用onStartCommand,其他未处理的intents依次分发。这种情况适合执行的任务需要理解恢复的场景,比如下载文件。
启动服务
startService –> onStartCommand
Intent intent = new Intent(this, HelloService.class);startService(intent);
startService方法立即返回。
如果service不能绑定,只能通过startService启动,而且需要发送返回值时,可以通过创建带延迟广播的PendingIntent(用getBroadcast)来启动服务,服务能用里面的广播来分发结果。
停止服务
如果服务同步地处理多个请求,那么不能在处理完一个请求直接销毁,有可能其他的请求刚发过来或者还在处理中。为了避免这种情况,你可以通过stopSelf(int)来确保关闭的服务就是刚处理完请求的,而不是其他正在处理的服务,整型参数唯一标识服务实例。
创建绑定服务
详见下一章节
发送Notifications给用户
服务一旦运行,可以通过Toast Notifications和Status Bar Notifications提醒用户。
在前台运行服务
在前台运行的服务必须提供一个状态栏的notification,来呈现运行状态,除非服务被停止或者移除,否则notification不会消失。
比如:播放音乐,在状态栏有个notification来告诉用户当前播放的歌曲,提供用户进入音乐播放器的入口
服务需要在前台运行,需要调用startForground,第一个参数是notification的唯一标示(不能为0),第一参数就是notification,比如:
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text), System.currentTimeMillis());Intent notificationIntent = new Intent(this, ExampleActivity.class);PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);notification.setLatestEventInfo(this, getText(R.string.notification_title), getText(R.string.notification_message), pendingIntent);startForeground(ONGOING_NOTIFICATION_ID, notification);
停止在前台运行,可以调用stopForeground。
实现生命周期回调
public class ExampleService extends Service { int mStartMode; // indicates how to behave if the service is killed IBinder mBinder; // interface for clients that bind boolean mAllowRebind; // indicates whether onRebind should be used @Override public void onCreate() { // The service is being created } @Override public int onStartCommand(Intent intent, int flags, int startId) { // The service is starting, due to a call to startService() return mStartMode; } @Override public IBinder onBind(Intent intent) { // A client is binding to the service with bindService() return mBinder; } @Override public boolean onUnbind(Intent intent) { // All clients have unbound with unbindService() return mAllowRebind; } @Override public void onRebind(Intent intent) { // A client is binding to the service with bindService(), // after onUnbind() has already been called } @Override public void onDestroy() { // The service is no longer used and is being destroyed }}
Note:不需要执行父类的实现。
完整的生命周期:onCreate —> onDestroy
活动的生命周期:onStartCommand/onBind —> onDestroy/onUnbind