查看原文
其他

活儿好又不纠缠的IntentService

2017-05-12 承香墨影 承香墨影

版权声明:

本公众号发布的所有文章,均属于原创,版权归本公众号所有。

未经允许,不得转载。

一、前言

Service 是 Android 四大组件之一,正常来说,我们直接使用 Service 就可以了。

但是 Service 存在几个问题:

  1. 默认不会运行在单独的进程中,而是和所在应用共用同一个进程。

  2. Service 也是在主线程中运行,所以一些耗时操作,依然需要单独开启线程去执行。

第一个问题其实并不是什么大的问题,但是正常来说,我们使用 Service 就是想在后台执行一些其他的操作,例如:下载等,而这些,又需要额外开启线程来完成任务,这样无形之中加大了我们的代码量。

而 IntentService 就是为这个而生的。

二、什么是IntentService

IntentService 是继承自 Service 的,所以它本质上还是一个 Service 。在 IntentService 内部维护了一个 WorkerThread 来专门处理耗时操作,实际上它会将所有 IntentService 的业务操作都放在 WorkerThread 中执行。

如果 start 了多次相同的 IntentService ,那么每一次 start 的任务,都会在 WorkerThread 中依次执行。而最让我们省心的是,IntentService 在执行完这些任务之后,会调用 stopSelf() 结束自己。

从官方文档可以了解到,一些 IntentService 的特点:

  1. 它会创建独立的 WorkerThread 来处理所有的 Intent 请求。

  2. 它会创建独立的 WorkerThread 来处理 onHandleIntent() 的实现代码,无需担心多线程的问题。

  3. 所有请求完成之后,IntentService 会自动停止。

  4. 它的 onBind() 默认返回 null,不要去实现它,不要用 bindService() 绑定一个 IntentService。

  5. 它的 onStartCommand() 提供了默认的实现,会将请求的 Intent 添加到队列中。

从上面的介绍可以了解到,在 IntentService 开启了一个独立的 WorkerThread 来完成具体任务的执行,而我们只需要将需要完成的业务代码,在 onHandleIntent() 中实现即可。

三、如何使用 IntentService

既然已经对 IntentService 有一个简单的认识了,那么接下来就实现一个最简单的 IntentService 的 Demo。

IntentService 本身已经帮我们实现了大部分逻辑,我们只需要在 onHandleIntent() 中实现我们的业务逻辑代码即可。

Demo 中给的例子,其实什么也没做,就是 Thread.sleep() 了一会,然后输出了一些 Log。这样就完成了一个最简单的 IntentService 。

IntentService 使用起来也非常的简单,只需要通过构造方法传递 String 类型的参数,用于指定 WorkerThread 的名字,然后和平常使用 Service 一样调用 startService() 即可。
startService(new Intent(MainActivity.this,MyIntentService.class));

IntentService 依然还是一个 Service,所以需要在 AndroidManifest.xml 中为它注册。

<service android:name=".MyIntentService"/>

这里快速对 MyIntentService 调用三次 startService(),我们来看看输出的结果。

可以看到,每次 startService 的 startId 都不相同,而 IntentService 每次执行完成当前任务之后,会根据 startId 调用 stopSelf() 方法来标记需要停止服务,当最终没有需要执行的任务的时候,才会 onDestory() 销毁自己。这个细节,下一小结会详细讲解。

三、IntentService 如何实现它的特性

既然已经了解了 IntentService 的特性和如何使用它,接下来不看看具体它是如何实现的,总感觉缺少点什么。

那么我们就来从源码的角度,看看它是如何实现的。本身整个类,也只有不到二百行代码,真实有效的代码也就那么十几行,非常好理解。

先来看看 IntentService 是如何为一个 WorkerThread 的。

可以看到它实际上是使用了一个 HandlerThread 来维护线程的,而在 HandleThread 中,内部已经维护一个 Looper,这里直接使用 HandlerThread 的 Looper 对象,便于在 IntentService 中去维护消息队列,而这里最终创建的 mServiceHandler 是属于 HandleThread 这个 WorkerThread 的。

而在 ServiceHandler 中,直接转发 Message 中携带的 Intent 对象,给 onHandleIntent() 方法去执行具体的业务逻辑。执行完成之后,立即调用 stopSelf() 方法停止自己,注意这里 stopSelf() 方法的参数是 arg1。

接下来看看当我们调用 startService() 的时候,IntentService 到底干了什么?

onStartCommand() 中直接调用了 onStart() 方法,而上面 stopSelf() 方法使用的 arg1 正是这个 Service 的 startId,也就是说,它是会指定 startId 来停止当前的此次任务服务。而 Service 如果被启动多次,就会存在多个 startId ,当所有的 startId 都被停止之后,才会调用 onDestory() 自我销毁。这也解释了为什么多次 start 同一个 IntentService 它会顺序执行,全部执行完成之后,再自我销毁。

最终会在 onDestory() 中调用 quit 去退出 Looper 的循环,释放资源。

四、查缺补漏

本身 IntentService 适用的场景,就是一种无状态的 Service,所以它是不支持 bindService() 方法去 bound 一个服务的。正如它的 onBind() 实现,直接返回一个 null。

当然如果你强制想要使用 bindService() 去实现 onBind() 方法,也是可以的,比较 IntentService 本身也是一个 Service,但是这样就不符合 IntentService 的设计规范,破坏了它使用完成之后自我销毁的理念,这个时候还是推荐使用正常的 Service 即可。

而如果确实有完成之后交互的需求,可以考虑使用 EventBus 或者 LocalBroadcastManager 这种通信的方式来传递数据,都是一种不错的方案。


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存