从零开始--系统深入学习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