Service详解

一、Service

service是一个可以在后台长时间运行操作但不提供用户界面的应用组件。

两种形式:

启动:当组件通过startService()启动服务,即处于启动状态。启动后,服务可在后台一直运行,即使启动服务的组件已被销毁也不受影响。服务通常只执行单一操作,且不会将结果返回给调用者。

绑定:当组件通过bindService()绑定到服务,即处于绑定状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至IPC操作。仅当与另一组件绑定时,绑定服务才会运行。多个组件可以同时绑定到该服务,当全部取消绑定后,该服务就被销毁。绑定服务要实现onBind()。

注意:服务在其托管进程的主线程中运行,既不创建自己的线程,也不再单独的进程中运行(除非另行指定)。因此不能执行任何CPU密集型工作或阻止性操作,如网络操作等。应在服务内创建新线程来完成这项工作,或者使用IntentService。

重要方法:

onStartCommond():通过startService()启动服务时,调用此方法,服务会立即启动并在后台一直运行。如果实现此方法,则在服务工作完成后,需要调用stopSelf()停止自己或其它组件调用stopService()停止服务。

onBind():另一组件通过调用bindService()与服务绑定时,系统调用此方法,此方法必须返回IBinder提供一个接口,以供客户端与服务端进行通信。如果不希望绑定,则返回null。AIDL也是通过这种方式进行实现的。

onCreate():首次创建服务时,在onStartCommond()或onBind()之前调用。如果服务已在运行,则不会调用此操作。

onDestroy():服务被销毁时调用此方法。一般用于清理所有资源,如线程、监听器、接收器等。

服务需要在清单中声明。但是官方建议为了确保应用安全性,要使用显示Itent启动或者绑定服务,且不要为其声明Intent过滤器。设置android:exported属性为false,确保服务仅适用于本应用。

二、启动服务

通过继承两个类来创建启动服务:

Service:所有服务的基类。扩展此类时,必须创建一个执行所有工作的新线程,因为默认下,服务将使用应用的主线程。

IntentService:Service子类,在子线程逐一处理所有启动请求。适用于不要求同时处理多个请求的服务。只需要实现onHandleIntent()方法,便于执行后台工作。

扩展IntentService:

大多数启动服务都不必同时处理多个请求,因此使用IntentService是最佳选择。IntentSercice执行以下操作:

1.创建默认的工作线程,用于在主线程之外执行传递给onStartCommond()的所有Intent;

2.创建工作队列,用于将Intent逐一传递给onHandleIntent()处理,这样就不用但担心多线程问题。

3.处理完所有启动请求后停止服务,因此不必调用stopSelf();

4.提供onBind()的默认实现(返回null);

5.提供onStartCommond()的默认实现,可将Intent依次发送到工作队列和onHandleIntent()实现。

注意,需要为服务提供构造器。

示例:

public class HelloIntentService extends IntentService {

  /**
   * 需要调用super方法的构造器,传入工作线程名称
   */
  public HelloIntentService() {
      super("HelloIntentService");
  }

  /**
   * IntentService在默认的工作线程调用这个方法处理intent,当这个方法返回时,intentService停止服务。
   */
  @Override
  protected void onHandleIntent(Intent intent) {
      // 通常在此做耗时操作,比如下载等等
      try {
          Thread.sleep(5000);
      } catch (InterruptedException e) {
          // Restore interrupt status.
          Thread.currentThread().interrupt();
      }
  }
}
一般只要实现构造函数和一个onHandleIntent()即可,如果重写其他方法(onBind()除外),需要调用父类实现,以便IntentService能够正确处理工作线程的生命周期。

扩展Service:

一般用于需要并发处理任务时才会调用此方法,但是目前很多人依旧实现的是这个,其实是不建议的。

public class HelloService extends Service {
  private Looper mServiceLooper;
  private ServiceHandler mServiceHandler;

  // 处理从线程中获得的Message
  private final class ServiceHandler extends Handler {
      public ServiceHandler(Looper looper) {
          super(looper);
      }
      @Override
      public void handleMessage(Message msg) {
          // 通常在此做耗时操作
          try {
              Thread.sleep(5000);
          } catch (InterruptedException e) {
              // Restore interrupt status.
              Thread.currentThread().interrupt();
          }
          // 使用StartId停止服务,以便在处理另一个任务时,不会停止服务
          stopSelf(msg.arg1);
      }
  }

  @Override
  public void onCreate() {
    // 启动线程运行服务。注意一般是在子线程中执行任务处理,因为服务一般运行在主线程,防止线程阻塞。
    // 同时使其具有后台优先级,因此CPU密集型工作不会阻塞主线程。
    HandlerThread thread = new HandlerThread("ServiceStartArguments",
            Process.THREAD_PRIORITY_BACKGROUND);
    thread.start();

    // 获得子线程的Looper
    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();
      // 对于每个请求,发送消息启动任务并且发送startId,因此当需求执行完我们可以知道要停止哪个工作。  
      Message msg = mServiceHandler.obtainMessage();
      msg.arg1 = startId;
      mServiceHandler.sendMessage(msg);

      // 如果服务被杀死,则返回START_STICKY,这样就会重启
      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();
  }
}
操作还是很烦的。onStartCommond()的返回值用于描述系统该如何在服务终止时继续运行(IntentService已做了处理)。必须是以下常量之一:

START_NOT_STICKY:如果系统在onStartCommond()返回后终止服务,除非有挂起Intent要传递,否则不会重建服务。是最安全选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。

START_STICKY:如果终止服务,则重建服务并调用onStartCommond(),但不会传递最后一个Intent。相反,除非有Intent要启动,否则服务会通过空Intent调用onStartCommond()。适用于不执行命令,但无期限运行并等待作业的媒体播放器。

START_REDELIVER_INTENT:如果终止服务,则会重建服务,并通过传递服务的最后一个Intent调用onStartCommond()。任何挂起Intent均依次传递。适用于要主动执行应该立即回复的作业(如下载)。

启动:通过startService()方法。系统会调用onStartCommond()方法,如果服务未运行,会先调用onCreate()。如果希望返回结果,可通过广播传递结果。多次启动会多次调用onStartCommond(),但要停止只需stopSelf()或者stopService().

停止:除非系统必须回收内存,否则不会主动停止或销毁服务。在onStartCommond()之后仍继续运行。因此必须用stopSelf()停止自己,或者别的组件stopService()。但是假如处理多个请求时有一个调用了stopSelf(),则会影响其他的请求。因此提供stopSelf(int)方法确保服务停止请求始终基于最近的启动请求,也就是说假如在调用stopSelf(int)之前服务受到新启动请求,ID就会不匹配,服务就不会停止。

注意:为了避免浪费系统资源和电池,应用必须在工作完后停止服务。如有必要,其它组件用stopService来停止服务。即使为服务启用了绑定,一旦服务受到对onStartCommond()的调用,仍需要亲自停止服务。

三、绑定服务

通过调用bindService()与其绑定,创建长连接,通常不允许其他组件通过startService启动它。如需与其他应用组件交互,或者IPC操作,则应使用绑定服务。创建绑定服务,首先要定义客户端与服务通信的接口,要实现onBind()回调方法,用以返回IBinder,用于定义与服务通信的接口。一旦客户端收到Ibinder,即可开始通过该接口与服务进行通信。

服务端可以同时与多个客户端绑定。完成交互后,客户端会调用unbindService()取消绑定,一旦没有客户端与之绑定,则服务会被系统销毁。

四、其他使用细节

1.向用户发送通知:通过Toast或者状态栏通知用户发生的事件。

2.在前台运行服务:其主要特点是,即使内存不足,系统也不会将其终止。前台服务必须为状态栏提供通知,除非服务停止或从前台移除,否则不能清除通知。例如酷我音乐,播放的音乐在状态栏显示通知。

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);
注意:NotificationId不能为0.(有人说为0是不显示在toast上,但是仍是前台的,我试过,好像不行)。

要从前台移除,使用stopForeground(),此方法采用一个布尔值,指示是否也移除状态栏通知。此方法不会通知服务。但如果正在前台运行的服务被停止时,通知也会被移除。





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值