Service 概述

         Service是一个长时间运行在后台,没有交互接口的应用组件。其它的应用组件可以用来启动一个service,即便用户从当前应用切换到另外一个应用中,被启动的服务仍旧能后在后台运行。另外组件可以通过与service绑定来进行通信,甚至是进行进程间通信。例如服务可能会被用来处理网络事务、播放音乐、文件i/o、与Content provider 进行交互等其他需要在后台处理的问题。

基本上服务可以分为两种形式。

Started

应用组件(例如activity)可以通过调用startActivity()来启动一个service。一但应用被启动,即使启动它的组件销毁了,service本身还能在后台运行下去。一般,一个启动的服务用来执行写简单操作,比如上传或下载一个文件,一般不会给调用者返回结果。当操作完成后,service应该自行停止。

 Bound

当应用组件调用bindService()后,Service与组件就bind到了一起。被绑定的service提供了个cs模式的接口来让组件与service进行交互操作,比如发送请求,获取结果,甚至是通过ipc进行跨进程的通信。被绑定的service依赖与其绑定的组件生存。多个组件可以绑定到同一个service,当所有绑定的组件都销毁掉后,service也同时会销毁。

Although this documentation generally discusses these two types of services separately, your service can work both ways—it can be started (to run indefinitely) and also allow binding. It's simply a matter of whether you implement a couple callback methods:onStartCommand() to allow components to start it and onBind() to allow binding

尽管在文章中分开讨论了service的两种类型,但service是可以以started和Bind两种方式来启动的。主要涉及到你是否实现一对回调方法:用来进行启动的onStartCommand()方法 和和用来进行绑定的onBind()

不管你的程序是started 或者 bound的或者即是started又是bound,任何应用组件都可以使用这个service(即使是从另外一个独立的进程中使用)。使用方式跟Activity的启动方式非常类似,都是使用一个Intent.当然service也可以在mainest中声明为私有的,

以防止其他的应用使用。详细请看Declaring the service in the manifest

注意:service 运行在它所在进程的主线程中。一般service不会创建它自己的线程也不会运行一个单独的进程但中,除非特别制定。那也就是说,如果你的服务是用来执行一些耗CPU的操作或其他阻塞式的操作(例如mp3的后台运行或网络工作),你最好能创建一个单独的线程。通过使用独立的线程,你可以降低ANR的风险同时让主线程专注与activies中的用户交互操作。

The Basics

为了使用service,你必须创建一个service的子类。在你的实现中,你需要覆写一些毁掉方法,这些方法是service生命周期的关键阶段,并且这些方便本事非常适合绑定到service。

最重要的回调方法就是下面的这些:

onStartCommand()
当其他组件,比如activity,通过调用startService()启动了一个service后,系统会回调这些方法。一但这些方法被执行过后,service就是被启动了,并且会无限期的运行在后台。如果你已经实现了这个方法的话,那么当你的service完成了他们的工作后,需要自己调用 stopSelf() or  stopService()来停止service.(如果你仅仅想要bind 到service,那你不需要实现这个方法)

onBind()

当其他的组件通过bindService()绑定到service()后onBind()会被系统调用。如果你实现了这个方法,你必须通过返回IBinder返回一个接口来实现Clinet同service的交互。你必须实现这个方法,但如果你不想bind你可以返回一个null

onCreate()

当service第一次创建(在执行onStartCommand() or onBind()之前)的时候系统会调用这个方法。如果service 正在运行,此方法不会被调用。

onDestroy()
当service不在使用并正在被销毁的时候系统会调用此方法。你的service应该实现这个方法来清除一些资源,比如一些线程,监听者,广播接收器。这是service收到的最后一次的回调。

如果service是通过 startService()被启动的(startService()会导致系统调用onStartCommand()),只有service调用stopSelf()或者其它的组建调用stopService()来停止来停止service。

如果service是通过 bindService()启动的的(onStartCommand()不会被调用了),只有当组建与service捆绑起来后sercie才会启动,一但没有任何Client与service捆绑起来,系统会销毁service。

For more information about when the system might destroy a service, see the Processes and Threading document

如果系统资源过低,那么service会被强制停止来释放资源,来给获取用户焦点的activity使用。如果service与用户activity捆绑在一起,service被杀死的几率会比较低。如果service声明在前台运行,service几乎不可能被杀死。否则,如果service已经被启动,并且是运行时间很长,系统会降低service在后台task中的位置,也就是说service被杀死的几率的增大会被增大很多。如果service被启动了,

你必须设计成系统会重新启动service。如果系统杀死了你的service,只要资源恢复后,service会重新启动(这取决与onStartCommand()的返回值,后再后面讨论)。要了解更多的关于系统如果销毁一个service,请看Processes and Threading

在下面的章节中,会讨论各种类型的service怎么创建,怎么在其它组建中使用。

Declaring a service in the manifest

类似与actiity,你必须在manifest中声明。

为了声明你的service你需要在<application>中声明<service>。例如

<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>

<service>标签中还可以添加其他属性,例如定义启动权限或service运行所在的进程。 android:name是唯一必须有的,它指定了service的名字。一但你的产品发布,就不应该修改service的名字属性,否则一些靠名字引用service的功能不能使用(阅读下面的博客,Things That Cannot Change)。

参见<service>察看更多关于service在minest中的声明。

与activity类似,service允许其他的组件通过过滤器来隐式的启动service。通过声明intent filter, 安装在手机上的其他应用程序的组件,可以通过startService()启动service,service声明的filter与其他应用程序传递过来的filter相匹配就可以。

如果你只是想你的service是本地使用,那么你不要支持任何的intent filters。没有过滤器,你只能通过显示的名字来启动。更多的信息请参看下面的starting a service

另外,你可以只是设置属性android:exported为false,你的service就只是本地使用了,即使service提供了intent filter也不行。

更多关于创建service的 intnet filter 的内容,请参看http://developer.android.com/guide/components/intents-filters.html

Creating a Started Service

service被其他的组建通过调用startService()启动后,会调用service的onStartCommand()方法。

当service启动后,他就有了一个独立与启动它的组建的生命周期,即使启动它的组建已经被销毁了,service仍旧可以运行在后台。如果service的工作完成了,service应该自己调用stopSelfI()结束自己,或者通过stopService()来销毁自己。

类似与activity这样的应用组建可以通过调用startService()启动service,调用startService()的参数Intnet用来指定启动那个service,并且把要传递给service的data包含在Intent中。在servcie的onStartCommand()中接受传递过来参数。

例如,假设activity想保存一些数据到线上数据库,activity可以可以启动一个companion service。在调用startService()方法的时候把数据存储在其参数intent中。在service的onStartCommand()方法中接收数据,连接到网络,然后执行数据库操作。当操作完成后,servcie 停止并销毁自己。


注意:service默认运行在声明service的应用程序的进程当中的主线程中。如果你的service执行一个耗时或阻塞型的操作,并且相同应用中的activity又负责与用户的交互,那service会拖慢activity的执行效率。为了防止这中情况,你应该在service中启动一个线程。

一般情况下,你可以继承两个类来启动一个service

Service

所有service的基类。当你继承这个类的时候,因为service在应用程序的主线程中,为了防止activity的执行效率被拖慢,所以最重要的是你创建一个新的线程来实现业务工作。

IntentService

这是Service的子类,每次启动一个工作线程来处理所有启动请求。如果你不要求你的service同时处理多次请求,这是个不错的选择。你唯一需要做的就是实现onHandleIntent()方法,在这个方法中你会收到每次请求的intent参数,你可以字后台对每次接收到的请求分别进行处理。

下面的章节教你怎么通过使用上面的类来实现自己的service


Extending the IntentService class

因为大多数的启动的service不要求同时处理多次请求(例如现实中的多线程方案),因此继承 IntentService来实现你的service可能是最好的选择

IntentService 会做下面的事情。

  • 创建一个默认的工作线程来处理所有传递给onStartCommand() 的intent参数,工作线程实现了与主线程的分离。
  • 创建一个工作队列传递给你的onHandleIntent() 方法,因此你永远也不用担心多线程。
  • 队列中所有的intnet处理完后自己会停止service,因此你不需要自己调用stopSelf()
  • 提供一个onBind() 的默认实现,在实现中返回null
  • 提供一个onStartCommand() 的默认实现,在实现将intent发送到工作队列,最后在onHandleIntent() 处理Intent.


上面的情况说明你只需要实现onHandleIntent()来处理Clinet请求的业务就可以了(尽管你可能需要提供一个小小的构造函数)。


这里是IntentService的一个列子

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) {
              }
          }
      }
  }
}

这就是你要做的一切:一个构造方法和 onHandleIntent()的实现。

如果你想覆写其他的回调方法如 onCreate()onStartCommand(), or onDestroy()不要忘记调用其超类的方法,以便IntentService可以处理工作线程的生命周期。

例如 onStartCommand() 你覆写的时候要返回其默认实现,(在那里实现了intent怎么传递给 onHandleIntent()

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

除了   onHandleIntent() ,唯一你不需要调用超类方法的就是 onBind()  (如果你的service允许bind你仅仅需要实现onBind()

在下一结中,你会看到如何通过继承Service类实现同样service,会使用多很多的代码。但如果你需要同时处理多个启动请求,也许这种方式更适合你。

Extending the Service class

就像你前面看到的,通过IntentService 使你实现Service变得非常简单。可是如果你要求你的service执行多线程(而不是通过工作队列处理启动请求),你可以通过继承Service 类来处理每一给intent。

为了做一个对比,下面的例子中通过继承 Service  来执行一个与上面的使用 IntentService 完全相同的工作。也就是说,对每一个启动请求,都使用一个线程来执行工作,在每次都处理一个请求。

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()请求,你可以同时执行多次请求。尽管例子中不是这样做的,但如果你想要这样做,你可以为每次请求创建一个新线程,并且立刻运行它(而不是等到前一次的请求处理完毕)。

你注意到onStartCommand()必须返回一个整数。这个整数告诉告诉系统当service被系统kill掉以后service应该如何处理service(在上面的讨论中 IntentService 已经默认实现了返回值)。onStartCommand() 必须是下面的返回值之一:

        如果  onStartCommand() 返回这个整数,系统kill掉service后不会重新创建service,除非有一个pending intents传递过来(unless there are pending intents to deliver)。 如果你的service在service kill后没有必要启动,或者可以简单的
重启未完成的工作,返回这个值是合适的。

         如果 onStartCommand()    返回这个值,那service被杀死后,系统会重新创建service并且会调用 l onStartCommand()  ,但不会接收上次的intent。也就说,   onStartCommand()    会接收一个null的intent,除非用一个service是用pending intents启动的才把intent传递过来。这个整数适合media players(或其它类似的service)这种没有正在执行命令,处于就绪带的service。【This is suitable for media players (or similar services) that are not executing commands, but running indefinitely and waiting for a job】

        如果 onStartCommand()  返回这个方法,系统在kill service后会调用 onStartCommand() 并且把最后一次的intent传递过来。所有的pending intent 都会轮番传递过来Any pending intents are delivered in turn.】这个返回值适合处于运行中,在杀死后应该立即恢复的情况,例如下载一个文件。

关于这值更多的解析,可以参见相关文档。



启动Service

你可以在从一个Activity或其他的组建中通过给 startService() 传递一个 Intent (指定哪个service被启动)来启动一个service。系统会调用service的 onStartCommand() 方法,并且把Intent 传递给它(永远也不要直接调用onStartCommand() 

例如,一个activity可以通过startService() 使用明确的intent启动前一节例子中的service。

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

startService() 会立刻返回,然后系统会调用onStartCommand() 方法。如果service没有运行,系统会先调用 onCreate() 然后再调用 onStartCommand() 。

如果service没有提供绑定,通过把intent传递给 startService()  是service与组件之间进行通信的唯一方式。可是如果你想要service返回一个result, 需要启动service的client创建一个   PendingIntent   然后把把它通过启动service的  Intent 传递给service。service然后可以通过使用broadcast来传递result.
多次请求启动servcie的命令都会传递到   onStartCommand() .  然而,只有一次的stop命令会停止service。可以使用(   stopSelf()  or  stopService() ) .

Stopping a service

一个启动的service必须管理它自己的生命周期。也就是说,除非系统因为内存原因会销毁service,系统不会停止service,service在 onStartCommand()  后会继续运行。所以系统必须通过调用 stopSelf()自己停止service ,或者其它组件通过调用 stopService()  来停止它。

一但 stopSelf() or stopService() 被调用,系统会尽快销毁service。

However, if your service handles multiple requests to onStartCommand() concurrently, then you shouldn't stop the service when you're done processing a start request, because you might have since received a new start request (stopping at the end of the first request would terminate the second one). To avoid this problem, you can use stopSelf(int) to ensure that your request to stop the service is always based on the most recent start request. That is, when you call stopSelf(int), you pass the ID of the start request (the startId delivered toonStartCommand()) to which your stop request corresponds. Then if the service received a new start request before you were able to call stopSelf(int), then the ID will not match and the service will not stop.

可是,如果你的service同时处理多个onStartCommand() 的请求,你不应该因为一个请求被完成后就停止service。因为你可能会有这样的情况,在你快结束的时候你会收到一个新的启动请求(第一个stop请求会决定第二个请求)。为了避免这种情况,你可以使用stopSelf(int)  来确保你的请求是被最后一个启动请求所结束。这也就是说,当你调用 stopSelf(int) 的时候,stop要求与你在onStartCommand() 中传递的id想对应才行。那样,如果service收到一个新的request在你可以调用 stopSelf(int) 之前,那么收到的ID不匹配,也就不会停止了。

注意:在完成你工作后,停止你的service是非常重要的,这样可以避免系统资源和延长电池的使用时间,如果必要的话,其它的组件可以通过调用stopService() 来停止service。即使servic可以bind,如果系统调用了 onStartCommand() 你也必须自己停止servic。

关于service的生命周期的内容,可以参看 Managing the Lifecycle of a Service

Creating a Bound Service


一个受约束的service允许应用组件通过调用bindService() 绑定到到它。这样可以创建一个长久链接(通常这个链接不允许其他的组件通过调用startService() 启动)。

如果你想从activity和其他在你应用程序中的组件与service交互,或通过进程间通信(IPC)在一个应用中向另外一个应用展示功能那么你应该创建bound service。

为了创建一个bound service, 你必须实现 onBind() 回调方法,返回一个 IBinder 接口,这个接口用来跟service之间通信。其他的接口通过bindService()  获取到这个接口,然后可以调用在这个service中的方法。service在生存期仅仅为bind到它的应用组件提供服务,因此当没有组件绑定到它的时候,系统会销毁它(你不要像通过onStartCommand() 启动service那样去停止一个service)。

为了创建一个bound service,第一件事就是你必须定义客户端跟服务的接口。这个接口必须是 onBind() 返回的IBinder 的子类。一但Client收到IBinder ,客户端就可以跟service之间进行交互了。 

多个客户端可以一次bind到service。当一个客户端与service交互完成,它要调用unbindService() 与service实现解除绑定。一但没有客户端绑定到service ,系统会销毁掉service。

有多种方法可以实现一个受绑定的service,并且实现起来比启动一个service复杂的多,因此bound service 在一个单独的 Bound Services 章节中。

Sending Notifications to the User


一但service运行,一个service可以通过Toast Notifications or Status Bar Notifications 通知用户。

toast 通知就是在屏幕上出现一个短暂的消息,然后消失。而状态条通知就是在状态栏提供一个带icon的消息。用户可以选择这个消息来执行一个动作(例如启动一个activity)。

通常情况,当一些后台工作完成展示一个状态栏是最后一种展现方式(例如一个文件下载完成),并且用户可以对它交互了。当用户选择了通知以后,这个通知可以启动一个activity(例如可以展示一个下载文件).

可以参见 Toast Notifications or Status Bar Notifications获取更多信息

Running a Service in the Foreground


前台service能主动的被用户意识到,并且当内存比较低的时候作为节省内存的候选者给杀掉。前台service必须提供一个通知给状态栏,通知被放置在“Ongoing”标题的下面。“Ongoing”的意思是除非service被停止会被从前台中去除,通知不能被打断。

例如,一个音乐播放器应该有一个前台service用来进行播放,因为用户很明确的知道它在运行。在状态栏的通知或许会指明当前正在唱的歌,允许用户启动一个activity让用户与播放器进行交互。

通过调用startForeground() 可以让service在前台运行。这个方法需要两个参数,一个用来唯一标示通知的数字,另一个是状态栏的 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, notification);

通过调用stopForeground() 可以将service从前台移除。这个方法返回一个boolean,用来表明是否把状态栏通知移除。这个方法不会停止service。可是如果你在service还在运行的时候停止service,notification也会被移除。

Note: The methods startForeground() and stopForeground() were introduced in Android 2.0 (API Level 5). In order to run your service in the foreground on older versions of the platform, you must use the previoussetForeground() method—see the startForeground() documentation for information about how to provide backward compatibility. 

注意: startForeground() and stopForeground() 是在Android2.0以后引入的,在以前的老版本中你必须使用setForeground() ,关于更都向后兼容问题,可以参看 startForeground() 的文档。

更多关于notifications 信息,参考 Creating Status Bar Notifications.


Managing the Lifecycle of a Service


service的生命周期比activity简单的多。可是你应该注意service的创建和销毁,因为service会偷偷的运行在后台。

The service lifecycle—from when it's created to when it's destroyed—can follow two different paths:

service的生命周期--从创建到销毁--可以看下面的两种形式。

  • A started service

    service在组件调用 startService() 以后被创建的。这种service会无限期的运行下去并且通过调用 stopSelf() 停止自身。另外其它的组件也可以通过调用stopService() 来停止service。当service停止后,系统会销毁 它。

  • A bound service

    在其它的组件调用 bindService() 的时候被创建。客户端与服务器通过 IBinder 进行交互。客户端通过调用unbindService() 来关闭与service的链接。多个客户端可以bind到同一个servcie,但所有的客户端都解绑后,系统销毁了service。(bound service 不需要停止自己.)

这两中方式不是完全独立的。也就是说,你可以绑定到一个已经用startService() 启动的service上。例如,一个后台音乐service可以通过其他组件调用 startService()  启动。随后,如果用户想要获取一些播放器的操控体验,或歌曲的播放信息什么的,activity可以通过 bindService() bind到service。在这种情况下, stopService() or stopSelf()  不会停止service,除非所有的客户端解绑。


Implementing the lifecycle callbacks

Like an activity, a service has lifecycle callback methods that you can implement to monitor changes in the service's state and perform work at the appropriate times. The following skeleton service demonstrates each of the lifecycle methods:

类似于activity,在service中有关于生命周期回调方法,在回调方法中,你可以监视service状态的的改变,在合适的时候执行work.下面的骨架service展示service生命周期的每一个方法。

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: 你可以不想activity的生命周期方法,你不用调用超类的方法。

By implementing these methods, you can monitor two nested loops of the service's lifecycle:

注意:尽管started类型的service被stopSelf() or stopService() 停止,但没有相应的回调操作(没有 onStop())。因此,除非service绑定到service,servcie停止以后系统会销毁它,onDestroy()  是唯一能收到的回调。

图2展示了典型的service回调方法。尽管图中分为  startService() 和 bindService() ,记住,任意的service,无论它是怎么启动的,都可能有客户端bind到它。因此一个在onStartCommand() 被初始化的service,可以收到在onBind()  中接收到回调。

更多关于bind创建service的信息,参见 Bound Services 文档,在Managing the Lifecycle of a Bound Service章节里面有很多关于onRebind()  回调方法的介绍。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值