Android编程权威指南(第二版)学习笔记(二十六)—— 第26章 后台服务
2017-02-23 12:59
417 查看
本章主要讲了 Android 的一大组件:服务。使用 IntentService 作为后台服务,用 AlarmManager 定时启动,以及应用通知的发出,还介绍了新的 JobScheduler 及其使用。
GitHub 地址:
完成第26章
Activity 就是 Android 应用的前台。所有应用代码都专注于提供良好的用户视觉体验。服务就是 Android 应用的后台,用户无需关心后台发生的一切。即使前台关闭,activity 消失好久了,后台服务依然可以持续不断地工作。
服务最关键的特性就是:用户离开当前应用后(打开其他应用或退回主屏幕),服务依然可以在后台运行。
初始创建的服务不会在后台线程上运行任何代码。而大多数重要服务都需要某种后台线程,IntentService 类提供了一套标准实现代码,所以推荐使用 IntentService 完成本章。
启动服务时调用一次。它有两个整数参数,一个是标识符集,一个是启动 ID。标识符集用来表示当前 intent 发送究竟是一次重新发送,还是一次从没成功过的发送。每次调用 onStartCommand(Intent,int,int)方法,启动 ID 都会不同。因此,启动 ID 也可用于区分不同的命令。
服务的类型由 onStartCommand(…)方法的返回值确定,可能的服务类型有 Service.
non-sticky 服务在服务自己认为已完成任务时停止。为获得 non-sticky 服务,应返回
通过调用 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 服务适用于长时间运行的服务,如音乐播放器这种启动后一直保持运行状态,直到用户主动停止的服务。
在外界使用 Context.startService(Intent) 即可开启服务。
PendingIntent 真正精妙的地方在于,将 PendingIntent token 交给其他应用使用时,它是代表当前应用发送 token 对象的。另外,PendingIntent 本身存在于操作系统而不是 token 里。如果不顾及别人感受的话,也可以在交给别人一个 PendingIntent 对象后,立即撤销它,让 send()方法什么也做不了。如果使用同一个 intent 请求 PendingIntent 两次,得到的 PendingIntent 仍会是同一个。我们可借此测试某个 PendingIntent 是否已存在,或撤销已发出的 PendingIntent。
AlarmManager.ELAPSED_REALTIME 是基准时间值 , 这表明我们是以 SystemClock. elapsedRealtime()走过的时间来确定何时启动时间的。也就是说,经过一段指定的时间,就启动定时器。假如使用 AlarmManager.RTC,启动基准时间就是当前时刻(例如,System. currentTimeMillis())。也就是说,一旦到了某个固定时刻,就启动定时器。
时间间隔由我们自己确定,不过推荐使用 AlarmManager 自身定义的常量。
Notification 需使用构造对象来创建。完整的 Notification 至少应包括:
在 Lollipop 之前的设备上,首次显示通知信息时,在状态栏上显示的 ticker text(Lollipop
之后,ticker text 不再显示在状态栏上,但仍与可访问性服务相关);
在状态栏上显示的图标(在 Lollipop 之前的设备上,图标在 ticker text 消失后出现);
代表通知信息自身,在通知抽屉中显示的视图;
待触发的 PendingIntent,用户点击抽屉中的通知信息时触发。
完成 Notification 对象的创建后,可调用 NotificationManager 系统服务的 notify(int, Notification)方法发送它。
首先,调用 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 地址:
完成第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 如下:public class PollService extends IntentService { private static final String TAG = "PollService"; // 外界获取服务的实例 public static Intent newIntent(Context context) { return new Intent(context, PollService.class); } public PollService() { super(TAG); } // 服务主要执行代码的地方 @Override protected void onHandleIntent(Intent intent) { Log.i(TAG, "Received an intent: " + intent); } }
在外界使用 Context.startService(Intent) 即可开启服务。
2. 使用 AlarmManager 定时启动服务
一个基本的定时启动代码如下:// 首先获取服务启动的 intent Intent i = PollService.newIntent(context); // 将其放入 PendingIntent 中 PendingIntent pi = PendingIntent.getService(context, 0, i, 0); // 获取 AlarmManager 服务 AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); // 如果开启服务 if (isOn) { // 将这个 PendingIntent 放到 AlarmManager 中定时启动 alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), POLL_INTERVAL, pi); } else { // 如果没有开启服务,就让 AlarmManager 撤销该定时器 alarmManager.cancel(pi); // 它自己也要撤销 pi.cancel(); }
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)方法发送它。
Resources resources = getResources(); Intent i = PhotoGalleryActivity.newIntent(this); PendingIntent pi = PendingIntent.getActivity(this, 0, i, 0); Notification notification = new NotificationCompat.Builder(this) .setTicker(resources.getString(R.string.new_pictures_title)) .setSmallIcon(android.R.drawable.ic_menu_report_image) .setContentTitle(resources.getString(R.string.new_pictures_title)) .setContentText(resources.getString(R.string.new_pictures_text)) .setContentIntent(pi) .setAutoCancel(true) .build(); NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); notificationManager.notify(0, notification);
首先,调用 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 发送两条消息,则第二条消息会替换掉第一条消息。在实际开发中,这也是进度条或其他动态视觉效果的实现方式。
相关文章推荐
- Android编程权威指南(第二版)学习笔记(二十三)—— 第23章 HTTP 与后台任务
- Android编程权威指南(第二版)学习笔记(二十八)—— 第28章 网页浏览
- Android编程权威指南(第二版)学习笔记(十八)—— 第18章 Assets
- Android编程权威指南(第二版)学习笔记(十一)—— 第11章 使用 ViewPager
- Android编程权威指南(第二版)学习笔记(二十)—— 第20章 样式与主题
- Android编程权威指南(第二版)学习笔记(十三)—— 第13章 工具栏(Toolbar)
- Android编程权威指南(第二版)学习笔记(二十五)—— 第25章 搜索
- Android编程权威指南(第二版)学习笔记(二十一)—— 第21章 XML drawable
- Android编程权威指南(第二版)学习笔记(十二)—— 第12章 对话框
- Android编程权威指南(第二版)学习笔记(二十九)—— 第29章 定制视图与触摸事件
- Android编程权威指南(第二版)学习笔记(十五)—— 第15章 隐式 Intent
- Android编程权威指南(第二版)学习笔记(二十二)—— 第22章 深入学习 intent 和任务
- Android编程权威指南(第二版)学习笔记(二)—— 第2章 Android 与 MVC 设计模式
- Android编程权威指南(第二版)学习笔记(四)—— 第4章 Android 应用的调试
- Android编程权威指南(第二版)学习笔记(三)—— 第3章 日志跟踪理解 Activity 生命周期
- Android编程权威指南(第二版)学习笔记(八)—— 第8章 使用布局与组件创建用户界面
- Android编程权威指南(第二版)学习笔记(十七)—— 第17章 Master-Detail 用户界面
- WINDOWS后台服务程序编写
- .Net的后台服务技术有哪些?
- Android 中提高后台服务优先级