zoukankan      html  css  js  c++  java
  • 从零开始系统深入学习android(实践让我们开始写代码Android框架学习2.service)

    第2章 Services

    Service是一个长期运行在后台,并不提供用户界面的应用程序组件。其他应用程序的组件可以启动一个service,并且即使用户切换到另一个应用程序,service也可以在后台继续运行。此外,一个组件可以绑定到service与它进行交互,甚至执行进程间通信(IPC)。例如,一个service可能会处理来自后台的所有事情,包括网络事务、播放音乐、执行文件I/O或者与content provider交互。

    一个service基本上有两种形态:

    1. 启动态(Started):当应用程序组件调用startService()方法来启动一个service时,service就处于“started”状态。一旦启动了service,即使启动service的组件被销毁,它也可以在后台运行下去。通常,一个已启动的service只执行单一的操作,且不会给它的调用者返回结果。例如,它可能通过网络下载或上传文件。当操作完成时,service应该要自行停止。

    2. 绑定态(bound):当应用程序组件通过bindService()方法与service绑定时,service就处于“bound”状态。一个绑定了的service会提供一个客户端-服务器(CS)接口,组件可以通过这个接口与service交互、发送请求、获取结果,甚至是跨进程的通信。只有当另一个应用程序与service绑定时,这个service才会运行。service可以与多个组件同时绑定,但是当它们全部解除时,service会被销毁。

       尽管上面分别介绍了service的两种类型,但你的service是可以在这两种情况下同时工作的,它可以在被启动的同时也能允许绑定。你只需要简单地执行这两种回调方法:允许应用程序组件启动service的onStartCommand()方法和允许绑定的onBinding()方法。无论你的应用程序是启动态、绑定态还是同时是这两种状态,任何应用程序组件都可以使用service(甚至从单独的一个应用程序中),同样的,任何组件也可以通过一个Intent来启动activity进而使用这个activity。另外,你可以在manifest文件中,将service声明为私有,并阻止来自其他应用程序的访问。

    注意:service是在宿主进程中的主线程上运行,因为service不会创建自己的线程,也不会在独立的进程上运行(除非你另行指定)。这就意味着,如果你的service要执行任何密集使用CPU的工作或者阻塞操作(如MP3播放音乐或网络工作),你就应该在service中创建一个新的线程来处理这些事情。通过使用一个独立的线程,你将减少应用程序无响应(ANR)错误的风险,并且能让应用程序主线程继续处理用户界面与activity的交互工作。

    2.1 基础知识

    要创建一个service,你就必须创建一个service的子类(或者一个现有的子类)。在实现过程中,你需要重新写入能处理service生命周期关键方面的回调方法,并且在适当条件下,提供一个将组件绑定到service的机制。下面是一些最重要的回调方法,你应该重写它们:

    1. onStartCommand():当另一个组件,如activity,调用startService()方法来请求启动service时,系统会调用这个方法。一旦这个方法执行,service就处于已启动状态,并且会在后台继续运行下去。如果你实现这个方法,当工作完成后,你应该使用stopSelf()方法和stopService()方法来停止service(如果你只是想提供绑定,那你就不需要实现这个方法)。

    2. onBind()当另一个组件想要通过调用bindService()方法与service绑定时,系统会调用这个方法。在这个方法的实现中,你必须提供一个接口让客户端与service通信,并返回一个Ibinder对象。你必须总是实现这个方法,但是如果你不想允许绑定,那么你应该返回null。

    3. onCreate():当service第一次被创建,用来执行一次性设置的程序时(在调用onStartCommand()或onBind()方法之前),系统会调用这个方法。如果service已经运行了,那么这个方法不会被调用。

    4. onDestroy():当service不再使用并且即将要被销毁时,系统会调用这个方法。你的service应该实现这个方法来清除如线程、注册监听器、接收器等的所有资源。这是service接收到的最后一个调用。

    讨论:我们到底应该使用一个service还是一个线程?

        一个service是即使用户不再与你的应用程序交互,它仍然可以在后台运行的一个简单组件。因此,你应该只要创建一个你真正需要的service。如果你需要执行在主线程以外的工作,但用户正在与你的应用程序进行交互,那么你应该创建一个新线程,而不是一个新的service。例如,如果你想播放一些音乐,但你的activty正在运行,那么你可以用onCreate()来创建一个线程,onStart()方法开始运行它,然后用onStop()方法来停止它。你也可以考虑使用AsyncTask或HandleThread,而不是传统的Thread类。要记住,如果你确实是要使用一个service,而它在默认情况下,仍然是在你的主线程上运行,那么如果你要执行密集型或阻塞操作,你应该还是要在你的service上创建一个新线程。

    如果一个组件通过调用startService()(它会导致onStartCommand()方法的调用)方法来启动service,那么service会一直运行,直到它使用stopSelf()方法自行停止了或者另一个组件通过stopService()方法停止了它。如果一个组件调用bindService()方法来创建service(onStartCommand()方法没有调用),那么只有组件与service绑定,它才会运行。一旦services从所有的客户端中解除,系统才会销毁它。只有在内存很低和系统必须恢复系统资源时,Android系统才会强制停止service。如果service被绑定到一个获取到用户焦点的activity,那么service是不太容易被kill掉,并且如果声明了service是在前台运行,那么它几乎是不会被kill掉。否则,如果service已经启动并长时间在运行,那么系统会久而久之降低它在后台任务列表中的位置,并且让它变得很容易被kill掉-如果你的service被启动了,那么你必须设计它怎样优雅的通过系统来处理重新启动的事件。如果系统kill掉了你的service,当他再次被重新启动后,那么资源很快就变得可用了(尽管这取决于从onStartCommand()方法中返回的值)。下面将介绍如何创建各种类型的service,以及如何从其他的应用程序组件中使用它。

    2.1.1  在manifest中声明service

       跟activity(和其他的组件)一样,你必须在manifest文件中声明所有的services。把一个<service>节点作为子节点添加到<application>节点中就可以声明你的service,如代码清单2-1所示:

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

    代码清单2-1

       <service>节点还有几个其他的属性,包括定义属性,如请求启动service的权限和service应该运行的进程。指定service类名的啊android:name属性是唯一必需的属性。一旦发布了应用程序,你就不应该更改这个名称,因为如果你改动了,你就可能破坏某些要用到隐式intents来引用service的功能。与activity一样,service也可以定义intent filters,它允许其他组件通过隐式的intents来调用service。如果你声明的intent filter与其他应用程序传递到startService()上的intent相匹配,那么安装到用户设备上的任何应用程序组件都可以启动service。如果你计划使用的是本地service(其他的应用程序不能使用它),那么你不需要(不应该)任何的intent filters。没有intent filter的情况下,你就必须使用一个有明确类名的intent来启动service。此外,只有把android:exported属性包括进去并且把它设置成“false”,你才能确保你的service是对你的应用程序私有。即便你的service有intent filters,你的service仍然是为你的应用程序私有。

    2.2 创建一个启动态的service

       一个启动态的service是另一个组件通过调用startService()方法启动的,它会导致services的onStartCommand()方法的调用。当service启动了,它就拥有了一个与启动它的组件相独立的生命周期,并且即使启动它的组件被销毁,service也会继续在后台运行。因此,当工作完成时,service可以调用stopSelf()方法来自行停止或者其他的组件通过stopService()方法来停止service。应用程序组件如activity,可以通过调用startService()方法来启动一个service,并给它传递一个Intent。这个intent指定了你要启动的service,并包含了service要用到的数据。service将会在onStartCommand()方法中接收到这个intent。例如,假设activity想把一些数据保存到一个在线数据库中。那么Activity可以启动一个service并通过一个intent来传递数据并使用startService()方法来保存的数据。这个service在onStartCommand()中接收到这个intent,与网络连接并执行数据库事务处理。当事务完成后,service会自行停止并会被摧毁。

    注意:针对Android 1.6或更低版,如果你要创建一个适合Android 1.6或更低版的应用程序,你需要实现onStart()方法,而不是onStartCommand()方法(在Android 2.0中,onStart()被弃用,它支持onStartCommand()方法)。还有默认情况下,service运行在与activity相同的进程中,并且是在这个应用程序的主线程上运行。所以,当用户与应用程序中的activity交互操作并且你的service要执行的密集或阻塞操作是在同一个应用程序时,service将会减弱activity的性能。为了避免影响到应用程序的性能,你应该在service中启动一个新的线程。

    传统来说,你可以继承下面两个类来创建一个启动态的service。

    1. Service:这是所有service的基类。当你继承这个类时,你可以在service中创建一个新的线程来处理service的所有工作,因为默认情况下,service会使用你的应用程序上的主线程,它会减弱应用程序中所有正在运行的activity的性能。

    2. IntentService:这是service的子类,它使用一个工作线程来处理所有启动的请求,每次只有一个线程存在。如果你不想service同时处理多个请求,那么这个类是最好的选择。你所需要做的只是实现onHandleIntent()方法,它会为每个启动service的请求来负责接收intent,这样你就可以在后台做这些工作。下面将介绍如何用这些类中的一个来实现你的service。

    2.2.1  继承IntentService类

        因为大多数启动的service不需要同时处理多个请求(实际上同时处理多个请求的情况是多线程),所以,实现service的最好办法也许是使用IntentService类。

    IntentService类会执行下面这些工作:

    1.创建一个默认的工作线程,在onStartCommand()方法中执行所有传递过来的intents,这个线程独立于应用程序的主线程。

    2.创建一个工作队列,在你实现的onHandleIntent()方法中,每次只会只会传递一个intent,这样你就不必担心多线程操作了。

    3.所有的启动请求都已经处理完了,就会停止service,这样你永远都不需要调用stopSelf()方法。

    4.提供一个onBind()方法的默认实现,他返回null。

    5.提供onStartCommand()方法的默认实现,这个方法会把intent发送给工作队列,然后在onHandleIntent()方法中处理它们。

    所有这些都说明了,你需要做的事只是实现onHandleIntent()方法来处理由客户端提供的工作(尽管你还需要为service提供一个小的构造函数),下面让我们看下代码清单2-2所示的例子:

    public class HelloIntentService extends IntentService {
      /** 
       * 需求一个构造函数,因为需要调用父类的构造函数方法,传入一个字符串 用来表示工作线程的名字
       */
      public HelloIntentService() {
          super("HelloIntentService");
      }
     
      /**
       * IntentService从默认的工作线程中调用这个方法并且传入了一个启动service的intent供我们使用。
       * 当这个方法返回时IntentService停止了service。
       */
      @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) {
                  }
              }
          }
      }
    }

    代码清单2-2

    上面就是你所需要做的:一个构造函数和onHandleIntent()方法的实现。如果你决定覆盖其他的回调方法,如onCreate、onStartCommand()或者onDestroy(),那么就一定要确保调用父类方法的实现,这样IntentService()方法才能处理好工作线程的生命周期。

    例如,onStartCommand()方法必须返回默认父类的实现(这是intent传递到onHandleIntent()方法的途径),如代码清单2-3所示:

    @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);
    }

    代码清单2-3

       除了onHandleIntent()方法之外,仅有一个onBind()方法你不需要调用父类的实现(但是,如果你的service允许绑定,你就只需要实现它)。下面将介绍,当继承service基类时是如何的实现相同的service,这个类的代码稍微多一点,但是如果你想处理同时发起的多个请求的话,这个类也许是适合的。

    2.2.2  继承service类

    正如前面所说的,用IntentService类来实现启动的service是非常简单的。然而,如果你想你的service能执行多线程操作(而不是通过工作队列来处理启动请求),那么你可以继承Service类来处理这些intents。经过比较,下面这个service类的实现代码,与上面那个使用IntentService类的示例所做的工作相同。也就是说,对于每一个发起的请求,它都能用一个工作线程来执行工作,并且每次只处理一个请求,如代码清单2-4所示:

    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停止service
              stopSelf(msg.arg1);
          }
      }
    
      @Override
      public void onCreate() {
        // 启动线程执行service.注意我们创建一个单独的线程是因为正常情况下service在主线程执行操作,可能会导致阻塞。   
        HandlerThread thread = new HandlerThread("ServiceStartArguments",
                Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();
        
        //获得HandlerThread的 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) {
          // 不需要绑定 返回null即可
          return null;
      }
      
      @Override
      public void onDestroy() {
        Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); 
      }

    代码清单2-4

       正如你看到的,这个类比使用IntentService类多了很多工作。然而,因为你能自行处理onStartCommand()方法的每次调用,所以你可以同时执行多个请求。这个例子没有这样做,但是如果你想这样做,那么你可以为每个请求创建一个新线程并立刻运行它(而不是等到上一个请求完成后才执行)。要注意的是,onStartCommand()方法必须返回一个整数。这个整数是一个值,它描述了当系统kill掉service时,系统应该如何继续这个service(onStartCommand()的默认实现会为你处理好,但你也可以修改它)。从onStartCommand()中返回的值必须是下面常量中的一个:

    1. START_NOT_STICKY:如果系统在onStartCommand()方法返回后kill掉service,那么不要重新创建service,除非有一个特定的intents要传递。这是一种最安全的选择,它避免执行不必要的service,并且你的应用程序能简单的重新开始未完成的工作。

    2. START_STICKY:如果系统在onStartCommand()返回后,kill掉service,那么就要重新启动service并调用onStartCommand()方法,但不用传递最后的intent。相反,系统调用onStartCommand()方法,传递一个值为null的intent,除非有一个用来启动service的特定intents,在这种情况下,这些intents就会被传递。这很适用于多媒体播放器(或类似的service),它们不需要执行命令,但是它还是会继续运行并且等待执行工作。

    3. START_REDELIVER_INTENT:如果系统在onStartCommand()方法返回后,kill掉services,那么就要重新启动service并且调用onStartCommand()方法,并将最后的一个intent传递给onStartCommand()方法。反过来,任何特定的intents都会被传递。这适用于service去执行那些要立即恢复的工作,如下载文件。

    2.2.3  启动一个service

    你可以通过给startService()传递一个Intent(指定要启动的service)来启动一个service,在activity和其他的应用程序组件中都能这样启动。Android系统调用service的onStartCommand()方法,并且把这个Intent传递给它(你应该永远都不要直接调用onStartCommand()方法)。例如,activity可以传递一个显式的intent给startService()方法来启动service,如代码清单2-5所示:

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

    代码清单2-5

       startService()会立即返回,然后Android系统会调用service的onStartCommand()方法。如果service没有正在运行,那么系统会首先调用onCreate()方法,然后才调用onStartCommand()方法。如果service也没有提供绑定,那么使用startService()方法是应用程序组件与service通信的唯一模式。如果你希望service返回一个结果,那么启动service的客户端可以为广播(用getBroadcast()方法)创建一个PendingIntent,并且在启动service的Intent中,把这个intent传递给service。然后service就可以用这个广播来传递一个结果。多个启动service的请求,会相应的导致多个service的onStartCommand()方法的调用。

    2.2.4停止一个service

       一个启动的service必须管理好自己的生命周期。也就是说,除非系统必须恢复它的内存,否则系统是不会停止或销毁service的,并且在onStartCommand()方法返回后service仍可以继续运行。所以,service必须通过调用stopSelf()方法才能自行停止,或者另一个组件调用stopService()方法来停止service。一旦用stopSelf()或stopService()方法来请求停止service,那么系统会尽快的销毁service。然而,如果你的service正在同时处理多个onServiceCommand()的请求,那么你就不应该在处理完一个启动请求后就停止你的service,因为你有可能已经接收到了一个新的启动请求(第一个请求结束时停止service,这将会终结第二个请求)。为了避免这个问题,你可以用stopSelf(int)方法来确保停止service的请求总是基于最后的启动请求。这也就是说,当你调用stopSelf(int)方法时,你会传递一个启动请求的ID(会传递到onStartCommand()方法中)来停止相应的请求。然后,如果service在调用stopSelf(int)方法之前接收到一个新的启动请求,那么这个ID就不会匹配,service也就不会停止了。

    注意:当service工作完成后,应用程序停止service是很重要的,因为要避免浪费系统的资源和消耗的电池。如果有必要的话,其他的组件也可以通过调用stopService()方法来停止service。如果你激活绑定了service,你也必须在onServiceCommand()方法的回调中不断的停止service。

    2.3 创建一个绑定的service

    绑定service是指:允许应用程序通过调用bindService()方法来绑定到它的一个service,这是为了建立一个长期的连接(一般不允许组件通过startService()来启动它)。当你想services与activity和其他组件交互时,或者想通过进程间通信(IPC)向其他的应用程序展示你的应用程序功能时,你就可以创建一个绑定的service。要创建一个绑定的service,你就选必须实现onBind()回调方法,使它返回一个IBinder,这个IBinder能定义与service进行通信的接口。然后其他应用程序组件就调用bindService()来检索这个接口,并开始调用service中的方法。只有当服务于它绑定的应用程序组件时,service才会存在,所以当没有组件绑定service时,系统就会销毁它(你不需要停止一个绑定的service,但是当service是通过调用onStartCommand()方法来启动时,你才必须停止它)。要创建一个绑定的service,首先要做的第一件事是定义一个客户端如何与service通信的接口。service和客户端之间的接口必须是一个IBinder的实现,并且必须是从onBind()回调方法中返回的。一旦客户端接收到IBinder,它就能通过接口与service进行交互了。多个客户端可以同时绑定到一个service。当某个客户端完成了与service的交互工作时,它就会调用unbindService()方法来解除绑定。一旦没有客户端与service绑定了,系统就会销毁service。

    2.4 向用户发送通知

       当运行service时,它可以用Toast Notifications或Status Bar Notifications向用户发送事件通知。一个toast通知是出现在当前窗口上面的一个信息,它显示一会就会消失,然而一个状态栏通知是提供一个带有信息的图标,它显示在状态栏里,这样用户要执行动作的话就可以选择它(如启动一个activty)。通常来说,当一些后台工作已经完成(例如,一个文件已经完成下载),一个状态栏通知是最好的技术,因为用户可以立刻执行那些后台操作。当用户从view中选择通知时,这个通知就可以启动一个activty(如查看下载好的文件)

    2.5 在前台运行service

    一个前台service是考虑做一些让用户特别关心的事情,并且它不是系统在低内存时要kill掉的候选。一个前台service必须为状态栏提供通知,并且放在持续性的标题栏的下方。这也就意味着通知是不会消失的,除非service它被停止了或把它从前台移除。例如,一个从service播放音乐的播放器应该设置成在前台运行,因为用户很关心它的操作。在状态栏中的通知可能会指示出当前播放的歌曲,并且允许用户启动一个activity来与音乐播放器进行交互。要使你的service能在前台运行,就调用startForeground()方法。这个方法有两个参数:一个int ID和一个用于状态栏Notification,如代码清单2-6所示:

    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);

    代码清单2-6

    要把前台的service移除的话,就调用stopForeground()方法。这个方法带有一个boolean值,它表示是否能同时移除状态栏通知。这个方法不能停止service。但是,如果你停止service,而它仍然在前台运行,那么这个通知也会被移除。

    注意:Android 2.0引用了startForeground()和stopForeground()方法(API级别5)。为了让你的service能在旧版本的平台运行,你必须使用先前的setForeground()方法。

    2.6 管理Service的生命周期

    Service的生命周期比activity的生命周期要简单的多。但是,由于service可以在后台运行,并且用户察觉不到,所以更进一步的了解service是如何被创建和销毁的就显得更加重要。

    从它被创建到被销毁,service的生命周期可以遵从下面两条路径:

    1.一个启动态的service:当另一个组件调用startService()方法时,service就会被创建。然后service会无限期的运行,要调用stopSelf()方法才能停止它。另一个组件也可以调用stopService()来停止service。当service停止,系统就会销毁它。

    2.一个绑定态的service:当另一个组件(一个客户端)调用bindService()方法,service就会被创建。这个客户端通过一个IBinder接口就可以与service通信。它也可以调用unbindService()方法来关闭这种连接。多个客户端可以与同一个service绑定,并且当所有的绑定都解除时,系统会销毁service(service并不需要自行停止)。这两条路径并不是完全分开的。也就是说,你可以绑定一个已经用startService()方法启动的service。例如,通过调用一个带有Intent的startService()方法,可以启动一个后台播放音乐的service,这个Intent会指定要播放的音乐。之后,用户可能想要对播放器进行控制或者获取当前歌曲的信息,那么activity就可以通过调用bindService()方法来绑定到这个service。在这种情况下,stopService()或stopSelf()方法会直到所有的客户端都解除,才能真正的停止service。

    2.6.1实现生命周期回调方法

        和activity一样,service有自己的生命周期回调方法,你可以实现这些方法来监测service的状态变化,并且还可以在适当的时候处理一些工作。以下是一个service的框架,展示了它的每个基本生命周期方法,如代码清单2-7所示:

    public class ExampleService extends Service {
        int mStartMode;       // service被killed掉后的启动模式
        IBinder mBinder;      // 用于客户端的绑定接口
        boolean mAllowRebind; // 是否需要重新绑定
    
        @Override
        public void onCreate() {
            // service 被创建
        }
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            // service运行中,处理startService()方法的回调
            return mStartMode;
        }
        @Override
        public IBinder onBind(Intent intent) {
            // 一个客户端使用bindService()方法绑定service后的回调
            return mBinder;
        }
        @Override
        public boolean onUnbind(Intent intent) {
            // 所有客户端使用unbindService() 解除绑定时的回调
            return mAllowRebind;
        }
        @Override
        public void onRebind(Intent intent) {
            // 一个客户端在onUnbind()调用后,在调用bindService()的回调 
        }
        @Override
        public void onDestroy() {
            // service不在被使用时
        }
    }

    代码清单2-7

    注意:与activity生命周期回调方法不同的是,你不需要调用service方法的父类实现。

    下面我们看下启动态和绑定态两种情况下的生命周期流程图,图2-1:

     

     

    图2-1 service的生命周期(左边演示的是用startService()方法来创建一个service时的生命周期,右边则是用bindService()方法创建一个service时的生命周期。)

     

    通过实现这些方法,你可以监测service生命周期中的两个嵌套循环:

    1. 整体生命期:一个service的整循环是在调用onCreate()方法和onDestroy()方法之间出现的。和activity一样,service在onCreate()方法中进行它的初始化设置,并用onDestroy()方法来释放所有剩余资源。例如,一个音乐回放service可以用onCreate()方法创建一个播放音乐的线程,然后用onDestroy()方法来停止这个线程。无论这两个方法是不是由startService()和bindService()方法创建,所有的services都会调用onCreate()和onDestroy()方法。

    2. 激活生命期:一个service的激活生命期开始于onStartCommand()和onBind()方法的调用。每个方法都会分别处理传给startService()方法或bindService()方法的Intent。如果service已经启动,那么service的激活生命期会在整体生命期结束时也跟着结束(即使onStartCommand()方法返回了,service仍然是激活的)。如果service是绑定的,那么激活生命期会在onUnbind()方法返回时结束。

    注意:尽管一个启动态的service可以通过stopSelf()方法或stopService()方法来停止,但service没有与此相应的回调方法(没有onStop()回调方法)。所以,除非service与一个客户端绑定,否则系统会在service停止时销毁它,而onDestroy()方法是service能接收到的使它停止的唯一回调方法。

    图2-1展示了service的一个典型回调方法。尽管该图把由startService()方法创建的service和由bindService()方法创建的service分开了,但要记住,不管service是如何启动的,它都能允许客户端绑定到它。所以,最初用onStartCommand()方法(客户端调用startService()方法)启动的service也仍然可以接收到onBind()方法的调用(当一个客户端调用bindService()方法时)。具体更详细的例子以后我们会在SDK下samples中讲解

    FAQ:QQ群213821767

  • 相关阅读:
    如何将网格式报表打印成其它样式
    拥有与实力不相称的脾气是种灾难——北漂18年(23)
    8.8.1 Optimizing Queries with EXPLAIN
    mysql 没有rowid 怎么实现根据rowid回表呢?
    secondary index
    8.5.5 Bulk Data Loading for InnoDB Tables 批量数据加载
    mysql 中key 指的是索引
    8.5.4 Optimizing InnoDB Redo Logging 优化InnoDB Redo 日志
    8.5.3 Optimizing InnoDB Read-Only Transactions 优化InnoDB 只读事务
    8.5.1 Optimizing Storage Layout for InnoDB Tables InnoDB表的存储布局优化
  • 原文地址:https://www.cnblogs.com/tianjian/p/2640514.html
Copyright © 2011-2022 走看看