`
Jonathan樊
  • 浏览: 77099 次
  • 性别: Icon_minigender_2
  • 来自: 上海
社区版块
存档分类
最新评论

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

 

2
0
分享到:
评论

相关推荐

    解析Google Android SDK——智能手机开发范例手册-下载代码

    总的来说,《解析Google Android SDK——智能手机开发范例手册》覆盖了Android开发的多个重要方面,从基础环境到高级技术,为开发者提供了全面的学习资源。通过深入研究这些章节,开发者不仅可以构建功能丰富的...

    Android开发——从小工到专家(书签)

    在Android开发领域,从新手入门到成为专家是一个不断学习和实践的过程。《Android开发——从小工...《Android开发——从小工到专家》书签版,无疑为这一过程提供了便利,让开发者可以迅速找到关键信息,提高学习效率。

    安卓Android源码——ServiceSample.rar

    在安卓应用开发中,`Service` 是一个非常重要的组件,它用于在后台执行长时间运行的操作,即使用户与应用程序没有交互。本篇将深入探讨 `ServiceSample` 示例代码,帮助你理解如何在 Android 中有效地使用 `Service`...

    安卓Android源码——GpsTracker源码.zip

    描述中的 "安卓Android源码——GpsTracker源码.zip" 与标题相呼应,暗示了这是一个关于Android开发的项目,特别关注于GPS跟踪功能的实现。源码的提供是为了让开发者能够深入理解如何在Android应用中集成和操作GPS...

    Android系统级深入开发——移植与调试 pdf

    《Android系统级深入开发...以上是《Android系统级深入开发——移植与调试》一书可能涵盖的主要内容,通过学习这些知识点,开发者能够掌握Android系统的核心技术,为创建高效、稳定且安全的系统和应用打下坚实基础。

    [Android Studio应用开发——基础入门与应用实战][方欣,杨勃][电子课件]

    【Android Studio应用开发——基础入门与应用实战】是针对初学者和希望提升Android应用程序开发技能的人群设计的一门课程。这门课程由专家方欣和杨勃共同编写,旨在通过电子课件的形式,深入浅出地讲解Android ...

    Android教材————

    在Android开发领域,掌握基础知识是至关重要的。这份"Android教材"包含了关于Activity生命周期、Service使用以及Content Provider的讲解,这些都是Android应用开发的核心概念。 首先,我们来深入理解第六讲——...

    Android学习之路——7.Service

    本篇文章将深入探讨“Android学习之路——7.Service”,分析Service的基本概念、使用方式以及常见应用场景,同时结合源码解析Service的工作原理,并提供一个实战Demo——Service_Demo。 一、Service基本概念 ...

    安卓Android源码——GoogleService.rar

    对于希望提升Android开发技能,尤其是与Google服务集成的开发者来说,深入研究这部分源码无疑会带来极大的收获。同时,这也是一种挑战,因为源码阅读需要扎实的编程基础和对Android系统架构的深入理解。但只要投入...

    安卓Android源码——应用开发揭秘源码.zip

    总的来说,通过深入学习《安卓Android源码——应用开发揭秘》中的源码,开发者不仅可以提升自己的编程技能,还能了解到Android系统的架构设计,从而在面对复杂问题时能够更有条理地分析和解决。这份源码资源是...

    Android 移动开发——打地鼠(Android Studio 版)Rat.zip

    【Android 移动开发——打地鼠(Android Studio 版)...这个“打地鼠”项目是学习Android开发的绝佳实践,它涵盖了Android开发的诸多核心概念,通过实际操作,开发者可以更深入地理解和掌握Android应用开发的各个环节。

    安卓Android源码——Gmail备份手机短信源码.zip

    在安卓(Android)平台上,开发人员经常需要处理各种任务,其中一项常见的需求是备份和恢复用户的短信。这个压缩包文件“安卓Android源码——Gmail备份手机短信源码.zip”提供了一个示例,展示了如何利用Android SDK...

    安卓Android源码——android Widget小组件开发.zip

    在安卓平台上,Widget小组件是应用与用户交互的一种方式,它们可以被添加到用户的主屏幕上,提供...这对于提高Android开发技能,尤其是对于那些希望为用户提供更直观、便捷服务的开发者来说,是一份宝贵的参考资料。

    安卓Android源码——app更新,实现service下载.rar

    在安卓(Android)平台上,开发一个能够实现应用自动更新的功能是一项常见的任务,它涉及到服务(Service)组件的使用...这涉及到了Android组件交互、网络编程、错误处理等多个方面,是Android开发中的一个重要技能。

    安卓Android源码——节奏大师.rar

    在Android开发中,源码是程序的核心部分,它包含了一系列的Java类文件和其他资源文件,开发者可以通过查看和修改源码来理解游戏的工作原理、实现功能以及优化性能。 【描述】"安卓Android源码——节奏大师.rar"提示...

    安卓Android源码——通讯录的开发_完整代码.zip

    本资源"安卓Android源码——通讯录的开发_完整代码.zip"提供了一个完整的通讯录应用开发案例,可以帮助开发者深入理解Android系统中关于联系人管理的底层机制以及UI设计的实践技巧。 1. **Android源码分析** - **...

    Android源码——sharedPref学习源码.zip

    在Android开发中,Shared Preferences是应用中保存轻量级数据的一种常见方式,它主要用于存储一些简单的键值对数据,如用户设置、应用状态等。在深入理解`sharedPref`的学习源码之前,我们先来回顾一下 Shared ...

    Android系统的Binder机制之一——Service_Manager

    ### Android系统的Binder机制之一——Service_Manager #### 一、引言 在深入探讨Android系统中的Binder机制之前,我们首先简要回顾一下Binder机制的基本概念及其重要性。Android系统基于Linux内核,但在进程间通信...

    安卓Android源码——安卓Android经典开发---豆瓣网移动客户端+讲解+源代码.rar

    总之,这份“安卓Android源码——安卓Android经典开发---豆瓣网移动客户端+讲解+源代码.rar”资料包为开发者提供了一个实践和学习Android开发的宝贵资源。通过深入研究豆瓣移动客户端的源码,开发者不仅可以掌握...

Global site tag (gtag.js) - Google Analytics