Android编程权威指南(第二版)学习笔记(二十六)—— 第26章 后台服务
本章主要讲了 Android 的一大组件:服务。使用 IntentService 作为后台服务,用 AlarmManager 定时启动,以及应用通知的发出,还介绍了新的 JobScheduler 及其使用。
GitHub 地址:
完成第26章
Activity 就是 Android 应用的前台。所有应用代码都专注于提供良好的用户视觉体验。服务就是 Android 应用的后台,用户无需关心后台发生的一切。即使前台关闭,activity 消失好久了,后台服务依然可以持续不断地工作。
服务最关键的特性就是:用户离开当前应用后(打开其他应用或退回主屏幕),服务依然可以在后台运行。
1. 服务的使用
1.1 服务的能与不能
与 activity 一样,服务是一个有生命周期回调方法的应用组件。这些回调方法同样也会在主 UI 线程上运行。
初始创建的服务不会在后台线程上运行任何代码。而大多数重要服务都需要某种后台线程,IntentService 类提供了一套标准实现代码,所以推荐使用 IntentService 完成本章。
1.2 服务的生命周期
如果是startService(Intent)方法启动的服务,其生命周期很简单,并具有三种生命周期回调方法。
onCreate(...)方法:服务创建时调用。onStartCommand(Intent,int,int)方法:每次组件通过 startService(Intent)方法
启动服务时调用一次。它有两个整数参数,一个是标识符集,一个是启动 ID。标识符集用来表示当前 intent 发送究竟是一次重新发送,还是一次从没成功过的发送。每次调用 onStartCommand(Intent,int,int)方法,启动 ID 都会不同。因此,启动 ID 也可用于区分不同的命令。onDestroy()方法:服务不再需要时调用。通常是在服务停止后。 服务停止时会调用 onDestroy()方法。服务停止的方式取决于服务的类型。- 服务的类型由 onStartCommand(…)方法的返回值确定,可能的服务类型有 Service.
START_NOT_STICKY、START_REDELIVER_INTENT和START_STICKY。IntentService 是一种 non-sticky 服务。
1.3 不同类型的服务
non-sticky 服务
non-sticky 服务在服务自己认为已完成任务时停止。为获得 non-sticky 服务,应返回START_NOT_STICKY或START_REDELIVER_INTENT。两者区别在于,如果系统需要在服务完成任务之前关闭它,则服务的具体表现会有所不同。START_NOT_STICKY型服务说消亡就消亡了;而START_REDELIVER_INTENT型服务则会在资源不再吃紧时,尝试再次启动服务。通过调用 stopSelf()或 stopSelf(int)方法,我们告诉 Android 任务已完成。stopSelf() 是个无条件方法。不管 onStartCommand(…)方法调用多少次,该方法总是会成功停止服务。stopSelf(int)是个有条件的方法。该方法需要来自于 onStartCommand(…)方法的启动 ID。只有在接收到最新启动 ID 后,该方法才会停止服务。(这也是 IntentService 的后台工作原理。)
sticky 服务
sticky 服务会持续运行,直到外部组件调用 Context.stopService(Intent)方法让它停止。 为获得 sticky 服务,应返回 START_STICKY。
sticky 服务启动后会持续运行,除非某个组件调用 Context.stopService(Intent)方法停止它。如因某种原因需终止服务,可传入一个 null intent 给 onStartCommand(…)方法,实现服务的重启。sticky 服务适用于长时间运行的服务,如音乐播放器这种启动后一直保持运行状态,直到用户主动停止的服务。
1.3 服务的使用
一个最基本的 IntentService 如下:
1 | public class PollService extends IntentService { |
在外界使用 Context.startService(Intent) 即可开启服务。
2. 使用 AlarmManager 定时启动服务
一个基本的定时启动代码如下:
1 | // 首先获取服务启动的 intent |
2.1 PendingIntent
PendingIntent 是一种 token 对象。调用 PendingIntent.getService(…)方法获取 PendingIntent 时,我们告诉操作系统:“请记住, 我需要使用 startService(Intent)方法发送这个 intent。”随后,调用 PendingIntent 对象的 send()方法时,操作系统会按照要求发送原来封装的 intent。
PendingIntent 真正精妙的地方在于,将 PendingIntent token 交给其他应用使用时,它是代表当前应用发送 token 对象的。另外,PendingIntent 本身存在于操作系统而不是 token 里。如果不顾及别人感受的话,也可以在交给别人一个 PendingIntent 对象后,立即撤销它,让 send()方法什么也做不了。**如果使用同一个 intent 请求 PendingIntent 两次,得到的 PendingIntent 仍会是同一个。**我们可借此测试某个 PendingIntent 是否已存在,或撤销已发出的 PendingIntent。
PendingIntent.getService(...) 方法打包了启动服务的方法的调用。它有四个参数:一个用来发送 intent 的 Context,一个区分 PendingIntent 来源的请求代码,一个待发送的 Intent 对象以及一组用来决定如何创建 PendingIntent 的标志符。
2.2 使用 AlarmManager
我们用 AlarmManager.setInexactRepeating(…) 方法开启了定时启动,该方法同样具有四个参数: 一个描述定时器时间基准的常量,定时器启动的时间,定时器循环的时间间隔以及一个到时要发送的 PendingIntent。
- AlarmManager.ELAPSED_REALTIME 是基准时间值 , 这表明我们是以 SystemClock. elapsedRealtime()走过的时间来确定何时启动时间的。也就是说,经过一段指定的时间,就启动定时器。假如使用 AlarmManager.RTC,启动基准时间就是当前时刻(例如,System. currentTimeMillis())。也就是说,一旦到了某个固定时刻,就启动定时器。
- 时间间隔由我们自己确定,不过推荐使用 AlarmManager 自身定义的常量。
2.3 获取定时器激活状态
由于我们在代码中撤销定时器的同时也撤销了 PendingIntent,所以通过发送一个 PendingIntent.FLAG_NO_CREATE 标志给 getService 方法可以获取这个 PendingIntent 存在状态。
3. 通知
如果服务需要与用户沟通,通知信息(notification)总是一个不错的选择。通知信息是指显示在通知抽屉上的消息条目,用户可向下滑动屏幕读取。 想要发送通知信息,首先要创建 Notification 对象。
Notification 需使用构造对象来创建。完整的 Notification 至少应包括:
- 在 Lollipop 之前的设备上,首次显示通知信息时,在状态栏上显示的 ticker text(Lollipop
之后,ticker text 不再显示在状态栏上,但仍与可访问性服务相关); - 在状态栏上显示的图标(在 Lollipop 之前的设备上,图标在 ticker text 消失后出现);
- 代表通知信息自身,在通知抽屉中显示的视图;
- 待触发的 PendingIntent,用户点击抽屉中的通知信息时触发。
完成 Notification 对象的创建后,可调用 NotificationManager 系统服务的 notify(int, Notification)方法发送它。
1 | Resources resources = getResources(); |
- 首先,调用 setTicker(CharSequence)和 setSmallIcon (int)方法,配置 ticker text 和小图标。
- 然后配置 Notification 在下拉抽屉中的外观。图标的值来自于 setSmallIcon(int) 方法 , 而设置标题和显示文字则需分别调用 setContentTitle (CharSequence)和 setContentText(CharSequence)方法。
- 接下来,须指定用户点击 Notification 消息时所触发的动作行为。这里使用的是 PendingIntent。用户在下拉抽屉中点击 Notification 消息时,传入 setContentIntent(PendingIntent)方法的 PendingIntent 会被触发。
- 调用 setAutoCancel (true)方法可调整上述行为。一旦执行了 setAutoCancel(true)设置方法,用户点击 Notification 消息时,该消息就会从消息抽屉中删除。
- 最后,从当前 context 中取出一个 NotificationManagerCompat 实例,然后调用 Notifi- cationManagerCompat.notify(…)方法贴出消息。传入的整数参数是通知消息的标识符,在整个应用中该值应该是唯一的。如果使用同一 ID 发送两条消息,则第二条消息会替换掉第一条消息。在实际开发中,这也是进度条或其他动态视觉效果的实现方式。
GitHub Page: kniost.github.io
简书:http://www.jianshu.com/u/723da691aa42