3.3.2 服务组件的开发和使用

从代码结构上来看,构造和部署服务组件都很简单,就是构造一个android.app.Service的子类型,并将其相关信息写到配置文件中。一个简单的实现框架的示例如下:


//子类com.duguhome.test.SimpleService

public class SimpleActivity extends Service{

@Override

protected void onStartCommand(Intent intent,

int flags, int startId){

//如果是处理消息,根据intent实例来实现

}

@Override

protected IBinder onBind(Intent intent){

//这是一个纯虚方法,如果需要支持服务绑定,根据intent参数返回对应的IBinder对象,否则,

返回null

}

}

//在配置文件中添加组件信息

<application…>

<service android:name="SimpleService">

…#此处可以添加Intent filter信息

</service>

</application>


而从具体的实现出发,构造一个扮演调度者角色的服务组件需要实现的函数是Service.onStartCommand。[1]当调用组件通过Context.startService发出请求后,对应服务组件的Service.onStartCommand函数就会被调用。开发者需要在这里捕获相关的参数,进行所需的处理(流程可参见图3-7)。需要时刻铭记的是,在Android中,所有的组件都是在主线程上构造的。因此,Service.onStartCommand函数的执行会阻塞主线程。如果涉及数据库读写、网络通信、复杂运算等耗时操作,那么就需要将相关操作放入独立的进程或线程中去执行。

将组件放入独立的进程中,可以通过配置文件的process参数来实现,代码如下:


//在service配置信息中,增加process

<service android:name="SimpleService"

android:process=":a_unique_process_name"/>


但这样的方式会增加进程开销。因此,另一种可行的策略是在服务组件中另起一个独立的线程,将那些耗时又费力的操作交给它来打理。

最简单的实现策略是通过派生android.app.IntentService来执行服务组件中的处理逻辑。如图3-8所示,IntentService的实现原理非常简单。它本身是android.app.Service的子类,当启动时会在Service.onCreate函数中建立一个后台线程,该线程通过其独立的消息循环在后台等待事件。当Service.onStartCommand函数接到相关的Intent参数时,主线程会将其中的内容打包发送到后台线程中,在Service.onHandleIntent函数中进行处理。从实现来看,只需把本应放在Service.onStartCommand中执行的逻辑内容挪到Service.onHandleIntent中即可。

3.3.2 服务组件的开发和使用 - 图1

图 3-8 IntentService的工作原理图

当然,如果调用者通过Context.bindService函数来绑定服务组件建立连接,那么Service.onStartCommand函数就不再会被调用,取而代之的是需要妥善处理Service.onBind函数。Service.onBind函数会接收到通过Context.bindService函数发出的Intent对象,依照Intent对象的不同,Service.onBind函数会返回对应的IBinder对象(如图3-8所示)。

整个绑定过程就是一个异步操作,组件管理服务参与其中进行调度。Context.bindService不会等待绑定确认便直接返回,Service.onBind函数返回的IBinder对象,会通过回调android.content.ServiceConnection对象的onServiceConnected方法传递给调用者。调用者拿到这个IBinder对象后,就可以通过它与服务组件进行远程方法调用[2]

对于调用者而言,当不再需要与服务组件通信时,需要通过Context.unbindService方法关闭连接。当组件服务不再有连接后,组件管理服务会选择时机销毁该服务组件对象。

[1]onStartCommand是Android 2.0(Level 5)版本增加的函数,目的是增强对组件生命周期的控制,在本书的第5章会详细讨论这个主题。如果你所开发的应用需要兼容2.0以前的版本,就需要再另外重载onStart方法来实现对应的功能。

[2]如果明确知道调用组件和服务组件处于同一进程内,可以绕过IBinder所提供的进程间的通信机制进行类型转换,然后调用其中的方法传输数据。第5章的组件数据传输部分会介绍这种方式,但这种使用模式超出了服务组件的设计框架,因此不推荐使用。