现在的位置: 首页 > 综合 > 正文

Android开发指导文档(译)–Service

2014年02月22日 ⁄ 综合 ⁄ 共 10532字 ⁄ 字号 评论关闭

 

Services

Service是一个可以在后台执行长时间操作的程序组件,它不提供用户接口。一个应用程序可以启动一个service,即使用户切换到其它应用程序,它也会继续在后台运行。此外一个组件可以绑定到一个service来与它交互甚至是用户进程之间的通信(IPC)。比如说,一个service或许处理网络事务,播放音乐,执行文件I/O或者与一个content provider相互作用,所有的都在后台进行。

Service通常采用两种方式:

被启动(started):

当一个应用程序组件通过调用startService()启动它时,这个service称为started”。一旦被启动,一个service就可以在后台独立的运行,即使启动它的组件已经被destory。通常一个被启动的service执行一项单独的操作并为呼叫者(caller)返回结果。比如说,它或许通过网络下载或上传一个文件。当操作完成时,这个service将自动停止。

被绑定(bound):

当一个应用程序组件通过调用bindService()绑定到它上面时,这个service” bound”。一个绑定的service提供了一个客户-服务接口来允许这个组件与service交互,发送请求,获得结果甚至使用进程间通信来跨进程做这些。一个绑定的service一直运行直到有其他的组件绑定到它上面。多个组件可以一次性绑定到一个service,但是当它们所有的解绑定时,service将被销毁。

尽管本文档常常单独的讨论这两种方式,但是你的service都可以用这两种方式。它可以被启动(无限期运行)或者亦可以绑定。只取决于你是否实现了一对回调函数:onStartCommand()来允许组件启动它以及onBind()允许绑定。

无论你的应用程序是采用启动,绑定还是两者都有,任何程序组件都可以通过Intent使用这个service(甚至从一个单独的程序),如同任何组件都可使用activity。然而你可以在manifest文件将这个service声明为私有并阻止其他程序的访问。

Caution一个service在它的主进程中运行---service不会创建它自己的线程并且不会运行在单独的进程(除非用别的方法指定)。这意味着,如果你的service将做一些CPU密集工作或者模块化的操作(比如MP3重放或联网),你应该为service创建一个新的线程来做这些工作。通过使用单独的线程,你可以减少Application Not Responding (ANR)错误的风险并且应用程序的主线程可以依然专注于用户与你的activity的交互。

 


Service基础

为了创建一个service,你必须创建一个Service(或已存的子类)的子类。你还需要重写一些回调函数,这些函数常处理service关键点以及实现绑定的机制。你需要重写的几个重要的回调函数是:

onStartCommand()

当别的组件,如一个activity,调用startService()来请求的service被启动时由系统调用。一旦该方法执行,service将启动并在后台无限期的运行。如果你实现它,当在service工作完成后使用stopSelf()stopService()停掉它是你的责任。(如果你想使用绑定的方式,你无须实现这个方法。)

onBind()

当一个组件通过调用bindService()想要与service(比如执行RPC)绑定时由系统调用。在你的实现中,你必须提供一个接口 让客户端使用来与service通讯,并返回一个IBinder。你必须总是实现这个方法,但是如果你不想绑定时可以返回null

onCreate()

service被第一次创建时由系统调用,来执行一次性的设置步骤(在调用onStartCommand()onBind()之前)。如果service已经运行,该方法不会被调用。

onDestory()

service不再使用并被销毁时由系统调用。你的service必须实现这个方法来清空资源(比如线程,注册的监听器,receiver等)。如果一个组件通过调用startService()(这将导致调用onStartCommand())来启动一个service,这个service将一直运行,直到它自己调用stopSelf()来停止或者别的组件调用stopService()来停止它。

如果一个组件调用bindService()来创建这个service(onStartCommand()不会被调用),这个service一直运行直到有其他的组件绑定到它上面。一旦没有客户端绑定到这个service,系统就会destory它。

当内存过低,系统必须为用户关注的activity回收资源时,Android系统可以强行停止一个service。如果这个service被绑定到用户关注的activity时,它可能不会被kill。如果这个service被声明为run in the foreground,它将永远都不会被kill。否则如果service是长时间操作,系统会随着时间的推移降低其在task列表里的位置并且这个service将变的容易被kill---如果你的service被启动,你必须设计好,能使系统很好的处理restart。如果系统kill了你的service,在资源变的可用时,系统会restart你的service(尽管这依然取决于你从onStartCommand()中返回的值),

在下面的章节,你将看到如何创建各种类型的service以及如何从其他程序组件使用。

 

manifest中声明一个Service

如同activity,你必须在manifest文件中声明所有的service。为了声明你的service,为<application>元素添加一个<service>元素,例如:

 

还有一些其它的属性你可以包含到<service>元素来声明一些特性,比如启动service的权限,service应在哪个进程里运行。

正如一个activity,一个service可以定义intent filter来允许其它的组件通过implicit intent激活(invoke)service。通过声明intent filter,在设备上安装的其他程序的组件可以启动你的service,只要你的service声明的intent filter与其他应用程序传递给startService()intent匹配。

如果你打算局部使用你的service(其他应用程序不能使用),这时你不需要也不应该提供intent filter。没有intent filter,你必须通过一个指定了service类名称的intent来启动该service

另外,如果你包含一个android:exported属性并将其值设为false,你可以确保你的service对你的程序是私有的。即使你的service提供了intent filter,这个属性也会发挥作用。

 


创建一个 Started Service

一个start service是指由其它组件通过调用startService()启动的service,调用该方法会触发serviceonStartCommand()函数的调用。

当一个service被启动,它就有了独立于启动它的组件的生命周期,service可以在后台无限期的运行,即使启动它的组件被销毁。因此这个service必须在工作结束时自调用stopSelf()结束自己,或者有其他的组件调用stopService()来停止它。

一个应用程序比如activity可以通过调用startService()启动service,需要传递给方法一个指明了serviceIntent,并在其内部包含一些service使用的数据。这个serviceonStartCommand()方法内接受到这个Intent

例如,设想一个activity需要将一些数据存储到online数据库。这个activity可以启动一个companion service并通过传递给startService()方法的Intent将数据递交给serviceserviceonStartCommand()方法内接受到这个Intent,连接到互联网并执行数据库事务。当事务完成,service停止它自己并被销毁。

Caution: 在默认情况下service与其声明所在的应用程序程序在相同的进程中,且都在程序的主线程运行。所以在用户与在同一程序中的activity交互的时候,如果你的service执行密集或者中断性的操作,service将会降低activity的执行速度。为了避免影响程序运行,你该为service另起一个新的线程。

通常有两个类可以让你来继承来创建一个started service

Service

这是所有service的基类。当你继承这个类,你该创建一个新的线程来做所有的service工作,因为默认情况下下这个service使用你应用程序的主线程,这将降低你应用程序中运行的activity的效率。

IntentService

这个是Service的子类,它使用一个worker 线程逐个处理所有start请求。如果你不需要你的service同时处理多个请求,这将是最好的选择。你所要做的就是实现onHandleIntent()方法,该方法为每个启动请求接收Intent,所以你可以做后台工作。

下面的章节描述如何是用上面的类实现service

 

继承IntentService类:

因为大多数started service不需要处理同时处理多个请求(这实际上是一个危险的多线程方案),你使用IntentService的子类来实现你的service可能是最好的选择。

IntentService将做以下内容:

  •  创建一个默认的worker线程来执行传递给onStartCommand()的所有intent,以与主线程分离。
  •  创建一个工作队列用来逐个传递intent给你的onHandleIntent()实现,所以你永远不用担心多线程。
  •  当所有start请求处理后,stop所有的service,所以你无须调用stopSelf()
  •  提供一个对onBind()的默认实现,并返回null
  •  提供一个对onStartCommand()的默认实现来发送intent到工作队列然后传递给onHandleIntent()实现。

所有这些事实表明你所需要做的所有事是实现onHandleIntent()来做由Intent提出的工作。(虽然你也需要为service提供一个构造方法)

下面是一个IntentService的实现:

 

我们要做的只是提供一个构造方法并实现onHandleIntent()方法。

如果你决定重写其它的回调函数,比如onCreate()onStartCommand()或者onDestory(),确保调用父类的实现,以便IntentService可以适当的处理worker线程的声明。

比如,onStartCommand()必须返回默认的实现:

 

除了onHandleIntent(),你不需要调用父类的方法的是onBind()(但是如果你的service允许绑定,你只需要实现它)。

在下一节,你将看到在继承Service类时,相同类的service如何实现,它将需要更多的代码,但是如果你需要同时处理start请求,这也许将是必须的。

 

继承Service类:

如果你需要service来执行多线程(而不是通过一个工作队列处理start请求),这时你可以继承Service类来处理每个Intent

为了比较,下面的例子代码是一个继承Service类实现的方式,该方式与前面使用IntentService类的例子执行了相同工作。换句话说,为每一个start请求,它使用一个worker线程来执行工作并一次只处理一个。

 

如你所见,比起IntentService,你要做更多的事。然而因为你自己处理每个onStartCommand()的调用,你可以同时执行多个请求。本例中没有实现,但是如果你愿意,你可以为每个请求创建一个新的线程并立即运行它们(而不是等先前的请求结束)。

注意onStartCommand()方法必须返回一个整型。这个整型是用来描述系统在kill Service时如何继续这个service的值(如前面讨论的,IntentService的默认实现为你处理这些).onStartCommand()的返回值必须是下面中的一个:

START_NOT_STICKY:

如果在onStartCommand()返回之后系统kill这个service不要重新创建这个service,除非有intent提交。在不需要或你的应用程序可简单restart任何未完成的工作时这是避免运行你的service的最佳选择。

START_STICKY:

如果在onStartCommand()返回之后系统kill这个service,重新创建service并调用onStartCommand(),但是不要传递最后一个Intent。系统调用传入null实参的onStartCommand(),除非有待定(pendingIntentstart service,在此情况下,这些intent将被传递。

这适合于媒体播放器(或类似的Service),它们没有在执行命令,但是无限期的运行并等待工作。

START_REDELIVER_INTENT

如果在onStartCommand()返回之后系统kill这个service,重新创建service,调用onStartCommand()并传递给它递交给service的最后一个intent。任何待定(pendingintent依次被递交。这适合于积极的执行需要马上返回的工作的service,比如下载一个文件。

启动Service

你可以在activity或其它应用程序组件中通过传递Intent(指明要启动的service)给startService来启动一个serviceAndroid系统会调用serviceonStartCommand()方法并传递一个Intent

比如,一个activity可以使用一个explicit intent 来启动先前例子的service

 

 

startService()方法立即返回,Android系统调用serviceonStartCommand()方法。如果service还没有运行,系统会先调用onCreate()方法,然后调用onStartCommand()

如果这个service没有提供绑定,这个intent是应用程序组件和service之间沟通的唯一模式。然而如果你想service返回结果,启动service的客户端可以为broadcast(用getBroadcast())创建一个PendingIntent并将其放在启动serviceIntent中传递给service。这时service可以使用broadcast来传递结果。

多个start service的请求将导致多次service的相应onStartCommand()的调用。然而,只需要一个请求来stop service(使用stopSelf()stopService()

Stop一个service

一个被启动的service必须自己管理它的生命周期。换句话说,系统不会stop或者destory service,除非必须回收系统内存。所以service必须通过调用stopSelf()停止自己或让其他组件调用stopService()stop它。

一旦使用stopSelf()stopService()提交了请求,系统会尽快destroy它。

然而,如果你的service同时处理多个提交给onStartCommand()的请求,当你处理完一个start请求时,你不应该stop这个service,因为你也许已经接收了一个新的start请求(在第一个请求结束时stop将会终止第二个请求)。为了避免这个问题,你可以调用stopSelf(int)来确保你对servicestop请求总是基于最近的start请求。也就是说,当你调用stopSelf(int),你传递一个start请求的ID给对应的stop请求。然后如果service在你能够调用stopSelf(int)之前接收到一个新的start请求,这时这个ID将不会匹配,service也将不会stop

Caution重要的一点是:当你的应用程序的service完成工作后,为了避免系统资源的浪费以及电池的消耗,应该将其stop。如

果需要,其他的组件可以通过调用stopService()stop这个service。虽然你可以为你的service绑定,但是如果它曾接收

到一个onStartCommand()调用,你必须亲自stop这个service

 

 


创建一个Bound Service

一个bound service是允许程序组件用bindService()绑定到它上面,来创建一个长期存在的联系(并通常不允许组件通调用startService()start它)。

当你想要与来自你的应用程序中的activity和其它的组件的service相互作用或者通过进程间通信(IPC)对其他程序公开一些应用程序的功能,你可以创建一个bound service

为了创建一个bound service,你必须实现onBind()回调函数返回一个IBinder来定义与这个service通信的接口。其它的组件就可以通过调用bindServiceIO来检索这个接口并在service中调用方法。这个service只为绑定到它上的组件存在,所以当没有组件绑定到service上时,系统就会destory它(不同于通过onStartCommand()启动的service,你不需要自己stop这个service)。

为了创建一个bound service,你首先要做的是声明客户端如何与service通信的接口。在service和客户端之间的接口必须是一个IBinder的实现,并且必须由你的serviceonBind()回调函数返回。一旦客户端获得这个IBinder,它就可以通过接口与service通信。

多个客户端可以一次性绑定到一个service。当一个service完成了与service的联系,它调用unbindServie()来解除绑定。一旦没有客户端绑定到service上时,系统就会destory这个service

有很多方法实现bound service并且这些实现都比started service实现复杂,所以bound service的讨论将在另一个单独的文档。

 


发送一个通知给用户

一旦运行,service就可以通过Toast NotificationStatus Bar Notification来通知事件的用户。

toast notification是一个信息,该信息出现在当前窗口的表面,显示一会就会消失。而一个status bar notification为含有信息的状态栏提供了一个图标,用户通过选择来执行一个action(比如启动一个activity)。

通常在一些后台工作完成后(比如文件下载完成),status bar 通知是通知用户的最好的手法了。当用户选择了展开的视图的通知时,这个通知可以启动一个activity(比如显示下载好的文件)。

 


在前台运行一个Service 

前台service是用户需要知道并且在系统缺乏内存时不作为候选的service。前台service必须提供一个为status bar使用的notification(通知)。该通知被置于”Ongoing”标题下,这意味着这个通知不能被解除,除非这个service stop或从前台移除。

抱歉!评论已关闭.