Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1085682
  • 博文数量: 169
  • 博客积分: 12306
  • 博客等级: 上将
  • 技术积分: 1299
  • 用 户 组: 普通用户
  • 注册时间: 2006-08-29 14:55
文章分类

全部博文(169)

文章存档

2012年(18)

2011年(78)

2010年(15)

2009年(1)

2008年(11)

2007年(39)

2006年(7)

我的朋友

分类: LINUX

2011-10-20 13:55:27

服务 Services

服务是一个应用组件,他可以在后台执行长时间的操作,并且不需要提供用户接口。别的应用组件可以启动一个服务,他将继续运行,即使用户已经切换到其它应用。此外,一个应用组件可以绑定到服务,并与之通信,甚至实现进程间通信 (IPC)。例如:一个服务可能处理网络传输,播放音乐,执行文件的 I/O处理,或者与 content provider交互,所有都来自于后台。

一个服务基本上可以采取两种形式:

Started

当一个应用组件通过调过 startService()启动了一个服务,服务便是 ”started”的了。一旦启动了,这个服务便可以在后***立运行,即使启动他的组件已被销毁。通常,一个 started的服务执行一个独立的操作,且不用给调用者返回结果。例如:他可能通过网络下载或者上载文件,完成之后自己停掉自己。

Bound

当应用组件通过 bindService() 来绑定一个服务,这个服务便是“ Bound”的了。一个绑定了的服务,提供了一个客户端到服务器的接口,他允许那个组件与服务通信,发送请求,接收结果,甚至跨进程间通信( IPC)。一个服务运行时间长度只与组件绑定他的时间长度一样。所有组件可以同时绑定他,但是当所有的组件都解除绑定关系,他就被销毁了。

你的服务可以以以上两种方式工作,是 Started的也可以是 Bound的。你实现一组回调是一件简单的事情, onStartCommand()允许你的应用组件去启动 (start) ,onBind()允许你的应用组件去绑定 (bind)他。

不论你的服务是 Started的, Bound的,还是两者都实现了的,任何一个应用组件 (甚至来自其它应用组件 )都可以访问他,和访问 Activity的其它组件的方式一样,用一个 Intent来启动他。然而,你也可以声明一个服务是私有的,在 manifest中,阻止来自其它应用的访问。

 

  基础 (The Basics)

创建一个服务,你必须创建一个 Service的子类 (或者 Serivce的子类的子类 )。在你的实现中,你需要重载一些回调方法去处理服务生命周期关键的方面,如果适合,为组件提供一个绑定到服务的机制。你应该重载的最为重的一些回调方法如下:

onStartCommand():

当别的组件,如 Activity,通过 startService()请求这个服务启动时调用该方法。一旦这个方法执行了,服务就是 Started的了,且他可以后***立运行。如果你实现了这个方法,那么你有责任在他完成工作之后调用 stopSelf()或者 stopService()停止掉他。 (如果你只想提供 binding ,你不需要实现这个方法 )

onBind():

当别的应用组件想通过 bindService()绑定到该服务时将调用这个方法(例如执行 RPC)。在你的这个方法的实现中,你必须通过返回 IBinder来提供一个客户端与服务器通信的接口。你通常实现这个方法,如果你不想被绑定,你可以返回一个 null

onCreate():

系统第一次创建将调用这个方法,以执行一些一次性的设置工作 (在执行 onStartCommand()或者 onBind()之前调用 )。如果服务已经在运行了,他不会被调用。

onDestroy():

当服务不再被使用,且即将被销毁时调用此方法。服务实现这个方法是为了清理资源,例如:线程,注册了的 listeners,广播接收者等等。这是服务接到的最后一个调用。

如果一个组件通过调用 startService()来启动一个服务 (onStartCommand()没有被调用 ),服务会保持运行到 stopSelf()使其停止,或者别的组件通过调用 stopService()使其停止。

如果组件通过 bindService()来创建这个服务 ( onStartCommand()没有被调用 ),服务运行的寿命与绑定到他的寿命一样。一旦客户端 unbind这个服务了,系统将会销毁他。

当处于低内存时,且持用用户焦点的 Activity必须获取系统资源时, Android系统会强制关闭服务的。如果服务时被绑定到持有用户焦点的 Activity时,他将不会优先被杀掉。如果服务被声明为 run in foreground,他将永远不会被杀掉。否则,如果该服务已启动,并长期运行的,那么随着时间的推移系统会降低其在后台任务列表中的位置,该服务将变得非常容易杀,如果您的服务已启动,那么你必须设计,它优雅的处理由系统重新启动 。如果系统杀掉你的服务,他会在资源再次可用时尽快重启。

 

manifest 中声明服务 (Declaring a service in the manifest)

 

Activity一样,你必须在你的应用中声明所有的服务。

声明一个服务,需要添加一个 元素作 元素的孩子。例如:

  1. <manifest ... >  
  2.   ...  
  3.   <application ... >  
  4.       <service android:name=".ExampleService" />  
  5.       ...  
  6.   application>  
  7. manifest>  
 

元素中,还有一些其它的属性你可以包括用来定义他的一些属性,诸如启动服务的权限,服务应该在什么进程中运行。

Activity一样,一个服务可以定义 intent filters允许别个组件通过显式的 intent来激活它。通过声明 intent filter,安装在同一个设备上的其它组件可以隐式的启动你的服务,如果你的服务声明的 intent filter 与别的应用传递给 startService() intent相匹配。

如果你打算他你的服务仅在本地运行(别的应用不可以使用他),你不需要 (也不应该 )提供任务 intent filter。如果没有任何 intent filter,你必须要使用一个显示的服务的名称的 intent来启动这个服务。

另外,你可以包含一个 android:exported的属性,且设为 false,这样可以确保你的服务只对你的应用私有。这即使是包括了 intent filter时也是有效的。

  创建一个 Started 服务 (Creating a Started Service)

 

一个 started的服务是一个别的组件通过调用 startService()来启动的服务,且最终有一个到到 onStartCommand()方法的调用。

当一个服务是 started的,他的生命周期独立于启动他的那个组件,他可以无限期的在后台运行,即使启动他的那个组件已被销毁了。所以,当他的工作完成了,服务应该调用 stopSelf()来停止自己。或者由别的组件调用 stopService()来停止。

一个应用组件如 Activity可以通过调用 startService()来启动这个服务,通过传递一个 intent来指定这个服务,并且可以包含任何数据给这个服务使用。服务在 onStartCommand()中接收这个 Intent

照惯例,这里有两个类你可以扩展用以创建 started的服务。

Service

这是所有服务的基类。当你扩展这个类,去创建一个新的线程来所有的服务的工作是很重要的,因为默认情况下,服务将使用应用的主线程,他可能会使你应用的正在运行的 Activity的性能降低。

IntentService

这是一个 service的子类,他使用一个工作者线程处理所有的请求,一次处理一个。如果你不要求你的服务同时处理多个请求,这是你最好的选择。你需要做的所有工作就是实现 onHandleIntent()方法,他可以接收每个 start请求的 intent然后你可以开始后台的工作。

扩展 IntentService (Extending the IntentService class)

由于大多数服务不需要同时处理多个请求,那么可能让你的服务实现 IntentService类是最好不过的。

IntentService类实现如下工作:

·         创建一个默认的工作者线程,用来处理所有的传递给 onStartCommand() intent,这个线程独立于你的主线程。

·         创建一个工作队列,一次向你的 onHandleIntent()传递一个 intent,所以你不必关心多线程工作方式。

·         在所有的启动请求完成之后停止服务,所以你不必调用 stopSelf()

·         提供了一个默认的 onBind()的实现,他返回 null

·         提供了一个默认的 onStartCommand()的实现,他发送 intent到你的工作者队列,然后到你的 onHandleIntent()的实现中。

所有这些都加起来得到这样一个事实,所有你需要做的是实现 onHandleIntent()来完成客户端提供的工作。 (不过, 你还需要为这个服务提供一个小构造。

这里有一个实现 IntentService的例子:

 

  1. public class HelloIntentService extends IntentService {  
  2.   /**  
  3.    * A constructor is required, and must call the super IntentService(String) 
  4.    * constructor with a name for the worker thread. 
  5.    */  
  6.   public HelloIntentService() {  
  7.       super("HelloIntentService");  
  8.   }  
  9.   /** 
  10.    * The IntentService calls this method from the default worker thread with 
  11.    * the intent that started the service. When this method returns, IntentService 
  12.    * stops the service, as appropriate. 
  13.    */  
  14.   @Override  
  15.   protected void onHandleIntent(Intent intent) {  
  16.       // Normally we would do some work here, like download a file.  
  17.       // For our sample, we just sleep for 5 seconds.  
  18.       long endTime = System.currentTimeMillis() + 5*1000;  
  19.       while (System.currentTimeMillis() < endTime) {  
  20.           synchronized (this) {  
  21.               try {  
  22.                   wait(endTime - System.currentTimeMillis());  
  23.               } catch (Exception e) {  
  24.               }  
  25.           }  
  26.       }  
  27.   }  
  28. }  

 

你需要的所有是:一个构造函数和一个 onHandleIntent()的实现。

如果你决定实现其它的回调方法,如 onCreate(), onStartCommand(), onDestroy(), 需要确保要调用超类的实现,以确保 IntentService能适当的处理工作线程的生命。

例如: onStartCommand()必须返回默认的实现 ( intent如何传递给 onHandleIntent())


  1. @Override  
  2. public int onStartCommand(Intent intent, int flags, int startId) {  
  3.     Toast.makeText(this"service starting", Toast.LENGTH_SHORT).show();  
  4.     return super.onStartCommand(intent,flags,startId);  
  5. }  

 

除了 onHandleIntent(),从父类继承而来的只有 onBind()不需要你调用超类的实现 (如果你的服务允许绑定你只需实现你的内容 )

扩展 Service (Extending the Service class)

如果你的服务需要执行多线程,你需要继承 service类来处理每一个 intent

为了比较,下面的例子是从 service扩展而来,执行着与以上使用 IntentService完全一样的工作。为每个 start的请求,他用一个工作者线程执行这个工作,并且每次只处理一个请求。

 

  1. public class HelloService extends Service {  
  2.   private Looper mServiceLooper;  
  3.   private ServiceHandler mServiceHandler;  
  4.   // Handler that receives messages from the thread  
  5.   private final class ServiceHandler extends Handler {  
  6.       public ServiceHandler(Looper looper) {  
  7.           super(looper);  
  8.       }  
  9.       @Override  
  10.       public void handleMessage(Message msg) {  
  11.           // Normally we would do some work here, like download a file.  
  12.           // For our sample, we just sleep for 5 seconds.  
  13.           long endTime = System.currentTimeMillis() + 5*1000;  
  14.           while (System.currentTimeMillis() < endTime) {  
  15.               synchronized (this) {  
  16.                   try {  
  17.                       wait(endTime - System.currentTimeMillis());  
  18.                   } catch (Exception e) {  
  19.                   }  
  20.               }  
  21.           }  
  22.           // Stop the service using the startId, so that we don't stop  
  23.           // the service in the middle of handling another job  
  24.           stopSelf(msg.arg1);  
  25.       }  
  26.   }  
  27.   @Override  
  28.   public void onCreate() {  
  29.     // Start up the thread running the service.  Note that we create a  
  30.     // separate thread because the service normally runs in the process's  
  31.     // main thread, which we don't want to block.  We also make it  
  32.     // background priority so CPU-intensive work will not disrupt our UI.  
  33.     HandlerThread thread = new HandlerThread("ServiceStartArguments",  
  34.             Process.THREAD_PRIORITY_BACKGROUND);  
  35.     thread.start();  
  36.       
  37.     // Get the HandlerThread's Looper and use it for our Handler   
  38.     mServiceLooper = thread.getLooper();  
  39.     mServiceHandler = new ServiceHandler(mServiceLooper);  
  40.   }  
  41.   @Override  
  42.   public int onStartCommand(Intent intent, int flags, int startId) {  
  43.       Toast.makeText(this"service starting", Toast.LENGTH_SHORT).show();  
  44.       // For each start request, send a message to start a job and deliver the  
  45.       // start ID so we know which request we're stopping when we finish the job  
  46.       Message msg = mServiceHandler.obtainMessage();  
  47.       msg.arg1 = startId;  
  48.       mServiceHandler.sendMessage(msg);  
  49.         
  50.       // If we get killed, after returning from here, restart  
  51.       return START_STICKY;  
  52.   }  
  53.   @Override  
  54.   public IBinder onBind(Intent intent) {  
  55.       // We don't provide binding, so return null  
  56.       return null;  
  57.   }  
  58.     
  59.   @Override  
  60.   public void onDestroy() {  
  61.     Toast.makeText(this"service done", Toast.LENGTH_SHORT).show();   
  62.   }  
  63. }  

 

正如你看到的这样,他比使用 intentService多做许多工作。

然而,由于你在你的 onStartCommand()处理每一个调用,你可以同时执行多个请求。我们这个例子没有这样做,但是如果你想,你可以为每个请求创建一个新的线程,然后立即运行 (而不是等待前面的请求完成 )

注意到 onStartCommand()必须返回一个整型。这个整型值描述了系统应该如何继续这个服务,或者杀死他(像上面讨论过的一样, IntentService的默认的实现帮你处理了这个,尽管你可以修改他)。 onStartCommand()的返回值必须是以下值的其中之一:

START_NOT_STICKY

如果系统在 onStartCommand()返回之后杀掉这个服务,将不会重新创建这个服务,除非这里有其它 intent转递过来。这是最安全的选项,避免不必要的时候运行你的服务且当你的应用可以简单的启动任何没有完成的工作。

START_STICKY

如果系统在 onStartCommand()返回之后杀掉这个服务 ,将重新创建服务,并且调用 onStartCommand()方法 ,但是不会重新传递最后一个 intent。相反,系统调用用一个 null intent调用 onStartCommand(),除非这里有等等启动服务的 intent存在,在这种情况下,那些 Intent将会被传递。这适合媒体播放器(或类似的服务),不执行命令,但运行无限期等待合适的工作

START_REDELIVER_INTENT

如果系统在 onStartCommand()返回之后杀掉这个服务 ,将重新创建服务,并且调用 onStartCommand()方法 ,并将最后一个 intent传递给他。任何挂起的意图依次交付。它适合于一个服务正在执行一项工作,他应该立即恢复工作,如下载一个文件

 

启动一个服务 (Starting a Service)

你可以从一个 Activity或者别的组件通过传递一个 intent startService()来启动一个服务。 Android系统调用服务的 onStartCommand()方法,并将 intent传递给他 (你永远不需要直接调用 onStartCommand()方法 )

例如:一个 Activity可以使用显示的 intent startService()来启动前面的示例服务 (HelloService)

  1. Intent intent = new Intent(this, HelloService.class);  
  2.   
  3. startService(intent);  

 

startService()会立即返回,然后 Android系统调 用服务的 onStartCommand()方法。如果服务还没有运行,系统会首先调用 onCreate(),然后再调用 onStartCommand()

如果服务不提供绑定,在应用组件与服务之间唯一通信方式 就是将 intent传递给 startSerive()。然而,如果你想要你的服务发一个结果回来,客户端可以为一个广播( 使用 getBroadCast() )创建一个 PendingIntent,在 intent中传递给服务来启动服务。服务可以使用这个广播来传递这个结果。

许多请求去启动这个服务至使许多相应到 onStartCommand()的调用。然而,只需要一个停止服务的请求 (stopSelf() stopService())即可停止服务。

 

停止一个服务 (Stopping a service)

一个启动了的服务必须管理好他的生命周期。系统不会停止会销毁这个服务回收系统内存除外,服务将在 onStartCommand()结束后继续运行。所以服务必须自己通过调用 stopSelf()来停止它或者别的应用组件过调用 stopService()来停止他。

一旦通过 stopSelf()或都 stopService()调用来停止他,系统会尽快来销毁这个服务。

然而,如果你服务同时处理多个到 onStartCommand()的请求,你不应该在处理完一个请求后便停止掉他,因为这时你可能刚刚接到一个新的请求 (在第一个请求的最后阶段可能会终结前第二个请求 )。为了避免这个问题,你可以使用 stopSelf(int)来确保你的终止的请求总是基于最近的启动请求的。即是,当你调用 stopSelf(int),你传递启动请求的 ID(传递给 onStartCommand() startId)到你停止请求的相适应。如果服务在可以接受 stopSelf(int )之前接到一个新的启动请求,那个 ID将不会匹配,且服务不会被停止掉。

注:当完成了他的工作之后,停止掉你的服务是很重要的,他可以避免浪费系统资源和耗费电池。如果有必要,别的组件可以通过调用 stopService() 来停止掉。即使你的服务是绑定的 (binding) ,如果他也接受 onStartCommand() 的调用的话, 你也通常需要自己停止掉你的服务。

 

创建一个绑定的服务 (Creating a Bound Service)

一个绑定的服务允许应用组件为了创建长时间的连接而通过调用 bindService()来绑定到他 ( 并且通常不允许应用组件通过调用 startService()来启动他 )

当你想让你的服务与 Activity或者其它组件交互,或者暴露你的一些功能到其它应用,完成进程间通信,那么你应该创建一个绑定的服务。

创建一个绑定服务,你必须实现 onBind()方法来返回一个 IBinder,这个接口定义与如何与该服务通信。别的组件可以通过调用 bindService()来个获取这个接口,并且开始调用服务的其它方法。服务只为绑定到他的应用组件服务,所以当没有组件绑定到服务时,系统会销毁他。

创建一个绑定服务,第一件事是定义一个接口,指定客户端可以怎样与服务端通信。服务端与客户端之间的接口必须是一个 IBinder的实现,并且由你的 onBind()方法返回。一旦客户端获得了 IBinder,他可以通过这个接口与该服务进行交互。

可以同时有多个客户端绑定到这个服务。一旦一个客户端完成了与该服务的交互,调用 unBind()来解除这种绑定关系。一旦没有客户端绑定到该服务,那么系统就会销毁掉该服务。

 

给用户发通知 (Sending Notifications to the User)

一旦运行了,一个服务可以通过 ToastNotifications或者 Status Bar Notifications来给用户发通知。

一个 Toast通知是在当前的窗口上显示一个消息,过会儿便消失。一个 status bar 的通知是在 status bar中提供一个图标和一则消息,用户可以选中他从而发生一个动作,比如启动一个 Activity

通常,当一个后台工作完成了(例如下载文件完成了),用户可以在他上面作一些行动,此时最好的技术使用 Status Bar Notifications。当用户从扩展视图中选择这个通知时,那个通知可以启动一个 Activity(例如去显示已下载文件 )

在前台运行一个服务 (Running a Service in the Foreground)

一个前台服务被认为用户正在关注的某事,并且不会在低内存情况下作为杀死的候选。一个前台服务必须给 Status Bar提供一个通知,他用来放置“正在进行”的标题,它意味着,这个通知不能被清除除非这个服务被停掉了,或者从前台移掉了。

例如,一个音乐播放器从服务播放音乐,应该被设置为在前台运行,因为用户明确的知道他的操作。通知可能用来指示正在播放的歌曲,并且允许用户启动一个 Activity来与音乐播放器交互。

为了让你的服务在前台运行,需要调用 startForeground()。这个方法需要两个参数:一个作为该通知的唯一标识的整数,以及一个给 Status Bar提供的 Notification。例如:

 

  1. Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),  
  2.         System.currentTimeMillis());  
  3. Intent notificationIntent = new Intent(this, ExampleActivity.class);  
  4. PendingIntent pendingIntent = PendingIntent.getActivity(this0, notificationIntent, 0);  
  5. notification.setLatestEventInfo(this, getText(R.string.notification_title),  
  6.         getText(R.string.notification_message), pendingIntent);  
  7. startForeground(ONGOING_NOTIFICATION, notification);  

 

要从前台移除服务,需要调用 stopForeground()。这个方法需要一个 boolean参数,用以指定是否也同时移除掉 Status Bar的通知。这个方法并不停止掉这个服务。然而,如果你停止掉你的服务,而他正在前台运行, Status Bar的通知会被移动掉。

 

管理服务的生命周期 (Managing the Lifecycle of a Service)

服务的生命周期比 Activity的要简单的多。然而,更重要的是你要密切注意你的服务是如何创建和销毁的,因为服务可以在后台运行,而不被用户察觉。

服务的生命周期,从他创建到销毁,可以遵循两种不同的路径:

·         一个启动地服务 (A Started Service)

当别的组件调用 startService()时创建该服务。然后服务独立的运行,并且必须要通过调用 stopService()来停止他。别的组件也可以通过 stopService()来停止他。当服务停止了,系统便销毁他。

·         一个绑定服务 (A Bound Service)

当别的组件 (客户端 )调用 bindService()时,该服务创建了。客户端通过一个 IBinder的接口来与该服务通信。客户端可以通过 unBindService()来关闭这个连接。多个客户端可以绑定到同一个服务,当所有的客户端解除绑定了,系统才销毁这个服务。 (服务不需要停止他自己 )

这两种路径不是完全独立的。即是说,你可以绑定个通过 startService()启动了的服务。例如:一个后台的音乐服务可以通过 startService()来启动,他用一个 intent来识别那个播放的音乐。稍后,用户可能对播放器作一些控制,或者查看正在播放的歌曲的信息,一个 Activity可能通过 bindService()绑定到这个服务。在这种情况下, stopService()或者 stopSelf()在所有的客户端解除绑定之前并不能实际停止掉这个服务。

实现生命周期的回调 (Implementing the lifecycle callbacks)

Activity一样,服务也有生命周期回调方法,你可以实现这些回调方法以监视服务状态的改变,在适当的时候做出相应的工作。下面的骨架演示了生命周期的每一个方法:

 

  1. public class ExampleService extends Service {  
  2.     int mStartMode;       // indicates how to behave if the service is killed  
  3.     IBinder mBinder;      // interface for clients that bind  
  4.     boolean mAllowRebind; // indicates whether onRebind should be used  
  5.     @Override  
  6.     public void onCreate() {  
  7.         // The service is being created  
  8.     }  
  9.     @Override  
  10.     public int onStartCommand(Intent intent, int flags, int startId) {  
  11.         // The service is starting, due to a call to startService()  
  12.         return mStartMode;  
  13.     }  
  14.     @Override  
  15.     public IBinder onBind(Intent intent) {  
  16.         // A client is binding to the service with bindService()  
  17.         return mBinder;  
  18.     }  
  19.     @Override  
  20.     public boolean onUnbind(Intent intent) {  
  21.         // All clients have unbound with unbindService()  
  22.         return mAllowRebind;  
  23.     }  
  24.     @Override  
  25.     public void onRebind(Intent intent) {  
  26.         // A client is binding to the service with bindService(),  
  27.         // after onUnbind() has already been called  
  28.     }  
  29.     @Override  
  30.     public void onDestroy() {  
  31.         // The service is no longer used and is being destroyed  
  32.     }  
  33. }  

注:不像 Activity的生命周期的回调方法,不要求在你的生命周期回调方法中调用超类的相应方法。

通过实现这些方法,可以监视生命周期的两个嵌套的循环 :

·         onCreate() onDestroy()整个生命期 (entire lifetime) 。像 Activity一样,服务在 onCreate()中做一些初始化的工作在 onDestroy()中释放掉其所持有的资源。

所有的服务都调用 onCreate() onDestroy(),不论你的服务是通过 startService()创建还是 bindService()创建。

·         活动生命期 (active lifetime) ,当一个服务被 onStartCommand()或者 onBind()调用开始。每个方法都处理了通过 startSerice()或者 bindService()传递给他的 intent

如果服务是 started的,活动期的结束即是整个生命期的结束 (即使在 onStartCommand()结束之后仍然是活动的 )。如果服务是绑定的,那么活动期会在 onUnbind()之后结束。

注:尽管一个服务是通过 stopSelf() 或者 stopService() 来停止的,但是服务没有一个独立的回调 ( 没有 onStop() 的回调 ) 。所以,如果服务没有被绑定到一个客户端,当服务停止了系统将会销毁他, onDestroy() 是唯一接到的回调。


原文地址:http://blog.csdn.net/mshopping/article/details/6533826

阅读(1458) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~