Android高级开发第四讲--API之Service

来源:互联网 发布:淘宝店铺图片上传 编辑:程序博客网 时间:2024/05/18 00:39

博客出自:http://blog.csdn.net/liuxian13183,转载注明出处! All Rights Reserved ! 



今天主要跟大家翻译一下Service的内容,请慢慢阅读!


Service是一个应用组件,它可以在后台长时间的运行,而不产生用户界面。
一个应用程序可以开启一个Service,即使切换到另外一个应用程序,它依然会继续执行。
此外,一个组件可以绑定一个Service并与它进行交互,甚至进行进程间的通信(IPC)。
例如,一个Service可以处理网络传输,播放音乐,执行文件I/0流,或者与Cotnent Provider进行交互,这所有一切都是在后台进行的。

Service基本上有两种形式
Started
当一个应用程序组件(如Activity)调用"startService()"时,Service被开启。一旦开始,它就会一直在后台运行下去,即使创建它的组件已经被销毁。
通常情况下,启动的Service会执行一个单一的操作,不会返回结果给调用者。例如,它可以通过网络上传下载文件,操作结束后,服务就应该被停止。
Bound
当一个应用程序组件调用"onBind()"时,Service被绑定。一个绑定的Service提供客户端-服务端接口,允许组件与Service进行交互,发送请求,得到结果,进行进程间的通信(IPC)。
当一个应用程序组件绑定它的时候,它才正式开启。多个组件可以同时绑定到一个Service上,当所有组件解绑("unbind()")后,Service会被马上销毁。
虽然这篇文章分析了Service的两种类型,但两者也可以同时使用,它可以被启动(无限期运行),也可以绑定运行。
这是一个简单的问题,在于你是否实现了一个双向回调方法。通过"onStartCommand()"方法可以开户并绑定当前的Service。


无论你的Service被开启,绑定或者两者都进行了,每一个应用程序组件都可以使用它(甚至是两个不同的应用程序),同时每一个应用程序都可以用一个activity通过Intent来启动它。
然而,你可以通过mainifest文件将Service设为私有,阻止其他程序访问。接下来是更多讨论在mainifest文件声明Service的一部分。
注意:Service运行在设备进程的主线程中,Service不会创建自己的线程,也不运行在一个单独的进程里(除非你另有指定)。这就意味着如果你的Service要进行CPU密集型工作或阻塞操作(如播放MP3或网络服务),你需要为这个Service提供一个新的线程来继续工作。使用一个新的线程,就可以减少应用程序不反应(ANR)错误和使主线程能致力于Activity与用户之间的交互。
最基本的
你应该用一个Service还是一个线程
Service是一个可以在后台运行甚至用户没有与当前的应用程序交互的组件。因此,当你需要时你可以创建这样一个Service。如果你需要在主线程外执行工作,但只在用户交互时,这时你最好创建一个线程而不是一个Service。举例:如果你想播放一些音乐,只有当这些Activity在运行的时候,你可以在onCreate()方法里创建一个线程,在onStart()方法里运行,在onStop()方法里结束。也可以考虑AsyncTask和HandlerThread,取代传统的线程类。参考进程和线程文档获得更多有益信息。
记得当你使用Service时,它仍然默认在你应用主线程里运行,所以当你要处理一些密集或阻塞操作时,要在Service里再创建一条线程。为创建一个Service,你必须创建一个Service子类(或已存在的子类)。在你的实现方法里,你需要重写一些处理Service生命周期关键方面的回调方法,如果适应的话。最重要的回调方法莫过于以下几个:
onStartCommand()
当另一个组件像Activity需要通过startService()来启动Service的时候,系统会调用这个方法。一旦这个方法执行了,Service就立马启动,并在后台开始运行。当你实现它们,你要在工作停止时停止Service,就要调用stopSelf()或者stopService()方法(如果你只想提供绑定,那你就不需要实现这个方法)。
onBind()
当另一个组件如执行RPC需要通过bindService()方法绑定Service时,系统会调用这个方法。在你的实现方法里,你必须提供一个接口,用作客户端与Service进行交流,并返回一个IBinder对象。你必须实现这个方法,但如果你不允许绑定,那么你可以返回null。
onCreate()
当Service第一次被创建时,系统会调用它,来执行一次启动程序(在调用onStartCommand()或onBind()方法之前)。如果你的Service已经存在,则这个方法不会被调用。
onDestroy()
当Service不再被使用时,系统会调用这个方法来销毁Service。你的Service需要实现这个方法用来清除如线程,注册的监听器,通知等等一些资源。这是Service最后接收的一个回调方法。如果一个组件通过startService()来启动一个Service(同时也会调用onStartCommand()),那Service仍然会保持运行直到调用stopSelf()来停止它或另一个组件调用stopService()来停止它。
如果一个组件调用bindService()方法来创建一个Service(同时onStartCommand()方法没有被调用),那么Service只在这个组件处于激活状态时才运行。一旦客户端解绑了Service,系统就会销毁它。
查看进程和线程文档
在接下来的章节中,你会看到如何创建每种类型的服务,以及如何从其他应用程序组件中使用它。在manifest中声明一个服务类似的Activity(和其他组件),你必须声明所有的服务在您的应用程序的manifest中。
声明你的服务,要添加一个<service>的元素,作为<application>元素的子元素。例如:
<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>
还有其他可以包括在<service>元素中定义属性,如启动该服务所需的权限,服务应该运行的过程。android:name是唯一必需的属性,它指定的类名的服务。一旦你发布你的应用程序,你不应该更改此名称,因为如果你这样做,你可能会打破一些明显与它关联的意图(阅读博客文章,也是无法改变的事情)。
请参阅<service>元素,获得在manifest中声明的更​​多信息。

就像一个活动,一个服务可以定义意图过滤器,允许其它组件使用隐式意图来调用服务。如果您的服务定义了一个意图过滤器与另一个应用程序传递给startService()的意图相匹配,在用户的设备上安装任何应用程序,都可以通过声明的意图过滤器,来启动服务。如果您打算仅在本地使用您的服务(其他应用程序不使用它),那么你就不需要(也不应该)提供任何意图过滤器。没有任何意图过滤器,您必须使用意图准确的启动该服务。有关启动服务的更多信息,将在下面讨论。

此外,您还可以确保您的服务是私有的,只有当你的应用程序的Andr​​oid:exported的属性设置为“false”。这也是有效的,即使你的服务提供的意图过滤器。
要获得更多关于为service创建意图过滤器的信息,请参阅的意图和意图过滤器文件。

创建一个启动的服务
Andr​​oid 1.6或更低
如果你正在建立一个为Android 1.6或更低版本的应用程序,你需要实现OnStart(),而不是onStartCommand()(在的Andr​​oid 2.0 onStart()被弃用,转而使用的onStartCommand())。对于兼容Android的版本低于2.0的更多信息,请参阅onStartCommand()的文档。

启动一个服务:另一个组件开始调用startService(),引起在服务中调用onStartCommand()方法。当服务启动时,它有一个生命周期,独立于启动它的组件并可以无限期地在后台运行,即使启动它的组件被销毁。因此,服务完成了自己的工作就应该调用stopSelf(),或让其他组件调用它的stopService()方法。
一个应用程序组件如Activity可以通过调用startService()启动一个服务,并可传递一个区分此Service的意图,外带一些Service所需的数据。该服务在onStartCommand()方法中能过intent来接收数据。

例如:假设一个activity需要保存一些数据到线上数据库,那么这个activity 可以通过自己启动一个service,然后在service的startServie()方法里来保存传过来的数据。service会在onStartCommand()里接收到intent,连接网络,执行数据库事务,当事务执行完毕,service自动结束,然后销毁。
注意:service默认与当前应用运行在同一个进程中,所以当用户正与activity进行的时候,如果你的service执行密集或者阻塞的操作台,就会明显降低activity的活性。为了避免这种情况的发生,你应该在service里启动一个新的线程。
一般来说,在创建启动service时有两个类可以继承。
Service
这是所有service的基类。当你继承自这个类时,最好创建一个新的线程来操作service的任务,因为默认下它会在应用的主线程中运行,这可能会降低应用中正在运行activity的活性。
IntentService
这是service的子类,它使用工作线程来一次处理一个请求,如果你不一次性处理多个请求,那这个类是极好的。你该做的就是实现onHandlerIntent(),来接收每次请求的intent,在后台工作。接下来的部分是来说明怎么使用上面两个基类的。
继承IntentService
因为大多数service不用一次性处理多个请求(这实际是一个危险的多线程情况),所以这时最好使用IntentService。
介绍IntentService
在onStartCommand()创建一个默认的工作线程来执行应用主线程传递过来的所有intent。创建一个工作队列,每次发送一个intent到onHandlerInent()中,这样你就不用担心多线程的情况。所有请求都被处理完后,就会结束这个service,所以不用调stopSelf()。
提供一个默认的onBind(),返回值为null。
提供一个默认的onStartCommand()来把intent发到工作队列里,然后执行onHandleIntent()。
最终所有的落实工作都由onHandleIntent()来做(你还需要为service提供一个小的构造体制)。
下面是一个实现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(),onDestroy(),确保要调用super()来加载父类方法,这样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(),剩下的所有方法都要调用super方法,onBind()除外(但如果你的service要求实现bind(),那还是要实现滴)
继承Service
在接下来的部分,你可以看到各种继承service的类,一些有很多代码,但可以同时处理多个请求。
正如你看到的上个部分,使用IntentService来实现一个service是很简单的。但是,如果你需要service来执行多线程(而不是通过一个工作队列来启动请求),那么你就可以继承service来处理每一个intent。
为了比较,下面的例子都是继承自service来处理同种任务的。那就是,每个启动请求,它会使用工作线程来一次处理一个请求。
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(); 
  }
}
正如你所见到的,service中所做的工作比IntentService中多的多。
但是,因为你在onStartCommand()中处理每个调用,你可以一次处理多个请求。上面的例子不是这么干的,但是如果你想,你也可以为每个请求创建一个线程来处理(而不是等上一个请求结束)
注意onStartCommand()必须返回一个integer类型,这个integer值用来描述当系统杀死service时,应不应该在事件中重复这个service。
(正如上面所讨论的,默认的intentService为你处理这个,虽然你也可以修改它)。onStartCommand()的返回值必须是下面的几个值之一。
START_NOT_STICKY
如果系统在onStartCommand()返回后就杀死service,那么不要重建service,除非有很多需要处理的intent。这是最安全的选项来避免不必要时运行service,此时你的应用可以简单的简单的重新启动未完成的工作。
START_STICKY
如果系统在onStartCommand()返回后就杀死service,重建service同时调用onStartCommand(),但不传递上一个intent。相反,系统调用带空intent的onStartCommand(),除非有正要启动service的intent存在,在这种情况下,这些intent会被传达。这适用于媒体播放器(或简单的服务),不执行命令,但无限执行并等待工作。
START_REDELIVER_INTENT
如果系统在onStartCommand()返回后就杀死service,重建servic同时调用onStartCommand(),带着传递给service的上一个intent。任一有待处理的intent都会顺序执行。这适用于service所有动态做一个马上被恢复活动的工作,如下载一个文件。有关返回值的更多信息量,可以参考API文档。
启动一个service
你可能通过activity或其他应用组件,发送intent来启动一个service,安卓系统调用service的onStartCommand(),并传递给它一个intent(不能直接调用onStartCommand())。
Intent intent = new Intent(this, HelloService.class);
startService(intent);
startService()方法执行很快,然后执行onStartCommand()。如果service没有运行机制,系统还会先调用onCreate(),然后接着执行。
如果service不提供bind方法,那么startService()里的intent是组件与service之间的唯一通信。然而,如果你想让这个servicev发送一个返回值,那么启动service的客户端可以为一个方法创建一个PendingIntent,放在itnent里面,然后传送给service,service可以用这个广播发送结果。
多个请求来启动service,结果是在onStartCommand()中有多个响应,但只有一个停止方法。
停止service
一个启动的service必须管理自己的生命周期。这就是说,系统不会停止或销毁service,除非必须重启系统,那么service仍然可以在onStartCommand()后执行。因此,service必须通过调用stopService()或者其他组件调用stopService()来停止它的工作。
一旦调用stopSelf()或stopService(),那么service会被尽快销毁。
但是,如果service在onStartCommand()同时处理多个请求,当你处理完一个请求时,你不能停止这个service。因为你可能收到一个新的请求(在第一个请求结束后调用stopService()会结束第二个请求)。为了避免这个问题,你可以调用stopSelf(int)来确保停止的service是基于最近的start请求。原理是这样的,当你调用stopSelft(int)时,你把启动请求的ID(传给onStartCommand()的ID)给停止请求的ID。那当你在stopSelf(int)之前接收一个新的请求时,这个ID就不符合,service也不会停止工作。
注意:service执行结束关闭它是很有必要的,来避免浪费系统资源和电池。如果可以,用其他组件一样可以通过stopService()来停止这个service。即使你能绑定这个service,如果它的onStartCommand()被调用了,你也必须亲自关闭它。
获得更多有关service生命周期的信息,请参见service生命周期管理一文。
创建一个绑定的服务
一个绑定服务意味着应用组件可以通过bindService()来绑定服务并建立一个长连接(并且直接禁止其他组件通过startService()来启动)
当你想让service与其他activties和其他应用组件进行沟通,或者扩展你应用的功能到其他应用,那应该创建一个绑定服务通过IPC来实现。
要创建一个绑定服务,你必须实现onBind()回调方法,返回一个定义service通信接口的IBinder对象。其他应用组件可以通过调用bindService()检索接口来调用service里的方法。service依赖于绑定它的应用组件,所以当没有可绑定的组件时,系统就会销毁它(你不必像通过onStartCommand()启动service一样必须关闭serevice)。
要创建一个绑定服务,第一步要做的是定义客户端与service通信的接口。它是IBinder的实现类并且返回onBind()回调方法。一旦客户端收到这个IBinder,它就可以通过接口来与service进行通信。
多个客户端可以同时绑定一个service。规则是这样的:当一个客户端与service做完交互后,它调用unbindService()来解绑。一旦没有客户端与service绑定时,它就会被系统销毁。
有多种途径来实现一个绑定service,实现方法也比单纯启动一个service复杂的多,所以绑定服务在另一个叫Bound Services的文档有更详细的表述。
向用户发送通知
一旦运行,service会通过Toast通知或状态栏事件来通知用户。
Toast通知以消息的形式短暂的显示在当前屏幕,状态栏通知则会在状态条里提供图标和消息一起显示,用户可以自由选择。
通常,状态栏是通知用户任务执行完毕的最好技术,而且用户可以点击它。当用户选择它时,它会启动一个Activity(如查看下载文件)。
在前台启动服务
前台service是用户所知道的,并不会在内存不足时销毁。前台服务必须为状态栏提供一个通知,放在“正在运行”下面,意味着它只能被停止或清除,不然不会消失。
例如,音乐播放器的service被放在前台,因为用户要操作它。状态栏的通知表明用户可用播放器对当前歌曲进行操作。
为了让你的service运行在前台,要调用startForeground()。这个方法使用两个参数,integer类型标识通知和通知栏的唯一性。例如:
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);
要清除前台service,就要调用stopForeground()。使用一个布尔值表示是否同时也清除通知栏。这个方法不会停止service,但是如果你在service运行在前台时停止它,通知也会被清除。
注:startForeground()和stopForeground()都是在2.0(API Level 5)引进的。为了让service运行在前台,你必须提前使用setForeground(),详见startForeground()文档。
管理service的生命周期
service的生命周期要远比activity简单。但是你要非常重视service的创建和销毁,因为它可以在用户不可知的情况下运行在后台。
service生命周期,从创建到销毁有两种不同的路线。
a started service
当其他组件调用startService()时service被创建,service会无限期执行,因此要调用sotpSelf()。另一个组件也可以通过调用stopService()来停止这个service,当service停止后,系统就会销毁它。
a bound service
当其他组件调用bindService()时service被创建,客户端与service之间通过IBinder接口来沟通。客户端可以通过unbindService()来关闭连接。多个客户端可以绑定同一个service,当所有都解绑时,系统会销毁它(这个service不用自我回收)。
这两种路线不是完全独立的。也就是说,你可以bind一个通过startService()启动的service。例如,后台音乐service可以通过调用startService()来启动并无限期播放。晚一会儿,可能用户对歌曲进行操作,或者获得当前歌曲的一些信息,那么activity可以通过bindService()来绑定这个service。在这种情况下,stopService()或stopSelf()不会完全停止这个service,直到客户端解绑。
实现生命周期回调方法
就像一个activity,service也有一个像activity的生命周期一样,来监测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
    }
}
注:不像activity的生命周期那样,你可以不调用superclass实现的那些方法。


图2.service生命周期.下面图1表示用startService()来启动一个service,图2表示用bindService()来启动一个service。
通过实现这些方法,你可以监控service生命周期的两种嵌套循环。
整个生命周期从onCreate()开始,到onDestroy()结束。就像activity,service在onCreate()里初始化,在onDestroy()里释放资源。例如,音乐播放的后台service可以在onCreate()创建一个线程来播放音乐,在onDestroy()里停止这个线程。
onCreate()和onDestroy()无论是startService()还是bindService()启动的service都要被调用。
service的活动周期起于onStartCommand()或onBind()的调用。每个方法都分别通过startService()或bindService()来传递intent。
注:即使一个service被stopSelf()或stopService()停止了,却没有一个回调方法返回给service。所以,即使客户端绑定了service,service停止时系统会销毁它,onDestroy()是唯一收到回调方法的。
图2.演示的是典型的service回调方法。虽然要区分创建service的startService()和bindService,但不管怎样记住一点,service还是允许被绑定的。所以,一个通过startService()启动的service,一样可以绑定。
获得更多创建service的信息,请参阅Bound Services文档,里面有讲关于绑定生命周期管理的onRebind()回调方法。