Android开发——Service的学习(上)

        Service是Android的重要组件之一,能够在后台,并且不需要用户界面的组件。其他的应用程序组件可以启动一个服务,即使用户切换到另一个应用程序,服务依然可以运行。服务运行在主线程中,如果要完成一些耗时的或者阻塞的工作,开发人员可以在服务中创建一个新的线程来完成这些工作。

        Service从本质上分为两种类型:Started(启动)和Bound(绑定)。

        

        Started:

        当应用程序组件通过startService()方法启动服务时,那么服务是started状态。一旦启动,服务就会在后台一直运行下去。 通常,started的服务执行单一的操作并且不会向调用者返回结果。 比如,它可以通过网络下载或上传文件。 当操作完成后,服务应该自行终止。

        Bound:

        当应用程序组件通过bindService()方法绑定到服务时,服务处于bound状态。bound服务提供了一个客户端/服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至可以利用进程间通信(IPC)跨进程执行这些操作。 绑定服务的生存期和被绑定的应用程序组件一致。 多个组件可以同时与一个服务绑定,不过所有的组件解除绑定后,服务也就会被销毁。

        服务可以只属于一种类型,也可以同时属于两种类型,既可以启动,也可以绑定。关键在于是否实现两个回调方法:onStartCommand()方法允许组件启动服务,onBind()方法允许组件绑定服务。

        一:Service的重要方法

        为了创建服务,必须创建Service类(或其存在的子类)的子类。在实现代码中需要重写一些回调方法,用于处理服务生命周期的一些关键节点,并且为组件提供绑定服务的机制。最重要的需要重写的回调方法如下:

        onStartCommand():

        当其他组件,比如一个activity,通过调用startService()请求started类型的服务时,系统将会调用这个方法。一旦这个方法执行,服务就会启动并且在后台一直运行下去。如果你的代码实现了本方法,你就有责任在完成工作后通过调用 stopSelf() 或 stopService() 终止服务(如果你只想提供bind方式,那就不需要实现本方法)。

        onBind():

        当其它组件需要通过 bindService() 绑定服务时(比如执行RPC),系统会调用这个方法。 在这个方法的实现代码中,你必须返回 IBinder 来提供一个接口,客户端用它来和服务进行通信。 你必须确保实现本方法,不过假如你不需要提供绑定,那就返回null即可。

        onCreat():

        当服务第一次被创建时,系统会调用此方法,用于执行一次性的创建过程(在系统调用onStartCommand()和onBind()前,调用此方法。如果服务依然运行,那么不会再调用此方法。

        onDestroy():

        当服务不再被使用并且将要被销毁时调用此方法。服务应该调用此方法去清理一些资源,比如线程,注册监听,接受者等等。如果服务已经运行,此方法不会被调用。

        如果组件调用onStart()方法启动服务(这会导致onStartCommand()被调用), 那么服务将一直保持运行,直至自行用 stopSelf() 终止或由其它组件调用 stopService() 来终止它。

        如果组件调用 bindService() 来创建服务(那 onStartCommand() 就不会被调用),则服务的生存期就与被绑定的组件一致。一旦所有客户端都对服务解除了绑定,系统就会销毁该服务。

 

        二:在Manifest中声明Service

        与activity(及其它组件)类似,你必须在应用程序的manifest文件中对所有的服务进行声明。要声明你的服务,把 <service> 元素作为子元素加入到 <application> 元素中去即可。例如:

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

        在 <service> 元素中可以包含很多其它属性,比如定义启动服务所需权限、服务运行的进程之类的属性。 android:name 是唯一必需的属性——它定义了服务的类名。应用程序一经发布,就不得再修改这个类名。因为这么做可能会破坏某些显式引用该服务的intent功能。

        三:创建Started服务

        Started Service 是由其他组件调用startService()方法启动的,这会导致onStartCommand()方法被调用。当服务是started状态时,其生命周期与启动它的组件无关,并且可以在后台无限期运行。因此服务需要自行用 stopSelf() 终止或由其它组件调用 stopService() 来终止它。

        诸如activity之类的应用程序组件,可以通过调用 startService() 启动服务,并传入一个给出了服务和服务所需数据的 Intent 对象。服务将在 onStartCommand() 方法中接收到该 Intent 对象

        

        例如,假定某activity需要把一些数据保存到在线数据库中。此activity可以启动一个companion service 并通过传递一个intent到 startService() 的方法把需要保存的数据发送给该服务。该服务在 onStartCommand() 内接收intent,连接Internet,再进行数据库事务处理。当事务完成后,服务自行终止,并被系统销毁。

        传统做法,你可以扩展两个类来创建started服务:

 

        Service类:

        这是所有服务的基类。如果你要扩展该类,则很重要的一点是:请在其中创建一个新的线程来完成所有的服务工作。 因为服务默认是使用应用程序的主线程的,这会降低应用程序中activity的运行性能。

 

        IntentService类:

        这是 Service 类的子类,它每次启动一个工作(worker)线程来处理所有的启动请求。 如果服务不需要同时处理多个请求的话,这是最佳的选择。 所有你要做的工作就是实现 onHandleIntent() 即可,它会接收每个启动请求的intent,然后就可在后台完成工作。

 

        继承IntentService类

        因为大多数started服务都不需要同时处理多个请求(实际上在多线程一下是危险的),所以最佳方式也许就是用 IntentService 类来实现上述的服务。

        

        IntentService 将执行以下步骤:

 

  • 创建一个默认的工作(worker)线程,它独立于应用程序主线程,来执行所有发送到 onStartCommand() 的intent。
  • 创建一个工作队列,每次向实现的 onHandleIntent() 传入一个intent,这样你就永远不必担心多线程问题了。
  • 在处理完所有的启动请求后,终止服务,因此你就永远不需调用 stopSelf() 了。
  • 提供默认的 onBind() 实现代码,它返回null。
  • 提供默认的 onStartCommand() 实现代码,它把intent送入工作队列,稍后会再传给onHandleIntent() 实现代码。

      以上说明:要做的全部工作就是实现 onHandleIntent() 的代码,来完成客户端提交的任务。由于IntentService类没有提供控参数的构造方法,所以你还需要为服务提供一小段构造方法)。以下是例子:

public class HelloIntentService extends IntentService {

  public HelloIntentService() {
      super("HelloIntentService");
  }
  
  @Override
  protected void onHandleIntent(Intent intent) {
      // 通常可以在这里添加任务代码, 比如下载一个文件.
      // 作为小例子,我们让他休眠5秒钟.
      long endTime = System.currentTimeMillis() + 5*1000;
      while (System.currentTimeMillis() < endTime) {
          synchronized (this) {
              try {
                  wait(endTime - System.currentTimeMillis());
              } catch (Exception e) {
              }
          }
      }
  }
}

        继承IntentService类

        如上所述,利用 IntentService 来实现一个started服务非常简单。 不过,假如你的服务需要多线程运行(而不是通过一个工作队列来处理启动请求),那你可以扩展 Service 类来完成每个intent的处理。作为对照,以下例程实现了 Service 类,它执行的工作与上述使用 IntentService 的例子相同。确切地说,对于每一个启动请求,它都用一个工作线程来完成处理工作,并且每次只处理一个请求。

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

  // 处理从线程接受到的消息
  private final class ServiceHandler extends Handler {
      public ServiceHandler(Looper looper) {
          super(looper);
      }
      @Override
      public void handleMessage(Message msg) {
          // 通常可以在这里添加任务代码, 比如下载一个文件.
          // 作为小例子,我们让他休眠5秒钟.
          long endTime = System.currentTimeMillis() + 5*1000;
          while (System.currentTimeMillis() < endTime) {
              synchronized (this) {
                  try {
                      wait(endTime - System.currentTimeMillis());
                  } catch (Exception e) {
                  }
              }
          }
          // 根据startId停止服务,这样就确保不会在处理任务中间停止服务了 
          stopSelf(msg.arg1);
      }
  }

  @Override
  public void onCreate() {
    // 启动运行服务的线程.
    // 创建一个单独的线程,因为服务通常运行于进程的主线程中,可我们不想阻塞主线程。
    // 我们还要赋予它后台运行的优先级,以便计算密集的工作不会干扰我们的UI。
    HandlerThread thread = new HandlerThread("ServiceStartArguments",
            Process.THREAD_PRIORITY_BACKGROUND);
    thread.start();

    // 获得线程的Looper,用来创建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();

      // 对于每一个启动请求,发送一个信息去启动一个任务,并且传递startID,这样就会知道在任务结束时,停止哪一个请求服务。
      Message msg = mServiceHandler.obtainMessage();
      msg.arg1 = startId;
      mServiceHandler.sendMessage(msg);

      // 如果服务被杀死,在这里重启服务
      return START_STICKY;
  }

  @Override
  public IBinder onBind(Intent intent) {
      // 因为并不提供绑定,所以 return null
      return null;
  }

  @Override
  public void onDestroy() {
    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
  }
}

       

        四:启动服务

        从activity或其它应用程序组件中可以启动一个服务,调用 startService() 并传入一个 Intent (指定所需启动的服务)即可。Android系统将调用服务的 onStartCommand() 方法,并传入该 Intent。例如,一个activity可以用一个显式的intent通过 startService() 启动上一节的示例服务(HelloSevice):

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

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

        如果服务不同时支持绑定,那么通过 startService() 传入的intent将是应用程序组件与服务进行交互的唯一途径。 当然,如果你期望服务能返回结果,那启动服务的客户端可以创建一个 PendingIntent 来获得一个广播broadcast(利用 getBroadcast() ),并把它放入启动服务的 Intent 并传到服务中去。然后服务就会用这个broadcast来传递结果。

        多个启动服务的请求将会引发服务 onStartCommand() 方法的多次调用。不过,仅仅需要一个终止服务的方法(用 stopSelf() 或 stopService() )来停止服务。

        五:停止服务

        一个started服务必须自行管理生命周期。也就是说,系统不会终止或销毁这类服务,除非必须恢复系统内存并且服务返回后一直维持运行。 因此,服务必须通过调用 stopSelf() 自行终止,或者其它组件可通过调用 stopService() 来终止它。

        用 stopSelf() 或 stopService() 的终止请求一旦发出,系统就会尽快销毁服务。

        不过,如果你的服务要同时处理多个 onStartCommand() 请求,那在处理启动请求的过程中,你就不应该去终止服务,因为你可能接收到了一个新的启动请求(在第一个请求处理完毕后终止服务将停止第二个请求的处理。 为了避免这个问题,你可以用 stopSelf(int) 来确保终止服务的请求总是根据最近一次的启动请求来完成。 也就是说,当你调用 stopSelf(int) 时,同时将启动请求的ID(发送给 onStartCommand() 的startId)传给了对应的终止请求。这样,如果服务在你可以调用 stopSelf(int) 时接收到了新的启动请求,会会因为ID不一样,将不会终止服务

           注:主要是翻译官网的内容,因为最好的学习资料还是在官网上啊

          http://developer.android.com/guide/components/services.html

猜你喜欢

转载自eleanor-fan.iteye.com/blog/2067483