Android多线程之IntentService源码解析
想要了解 IntentService 的工作原理需要先对 Android 系统中以 Handler、Looper、MessageQueue 组成的异步消息处理机制以及 HandlerThread 有所了解,如果你还没有这方面的知识,可以先看我写的另外两篇文章:
Android多线程之Handler、Looper与MessageQueue源码解析
Android多线程之HandlerThread源码解析
在介绍 Service 时我们经常会说 Service 适合于完成一些在后台工作的任务,例如播放音乐、下载文件等,但 Service 默认是运行于 UI 线程的,如果想要依靠其来完成一些耗时任务,就需要自己来建立子线程,这相对比较繁琐,所以官方也为开发者提供了 IntentService 来解决这一问题
IntentService 有以下几个特性:
- IntentService 内部创建了一个工作线程,用于在子线程内执行传递给
onStartCommand()
的所有 Intent,开发者无须关心多线程问题 - IntentService 内部通过 HandlerThread 和 Handler 来实现异步操作
- IntentService 是以串行方式处理外部传递来的任务,即只有当上一个任务完成时,新的任务才会被执行
- 在处理完所有任务请求后会自动停止,因此不必手动调用
stopSelf()
方法 - 提供了
onBind()
的默认实现(返回 null) - IntentService 是四大组件之一,拥有较高的优先级,不易被系统杀死,因此适合于执行一些高优先级的异步任务
IntentService 总的代码不到一百行,如果你对 HandlerThread 有所了解,就可以很容易地了解 IntentService 的工作机制
先看下 IntentService 的类声明
IntentService 类是一个抽象类,包含一个抽象方法需要由子类来实现
public abstract class IntentService extends Service
包含的成员变量
private volatile Looper mServiceLooper;//关联了 mServiceLooper 的 Handler private volatile ServiceHandler mServiceHandler;//IntentService 内部使用的子线程的线程名private String mName;//用于决定 onStartCommand() 方法的返回值private boolean mRedelivery;
构造函数
//传入子线程名public IntentService(String name) {super();mName = name;}
当 IntentService 第一次被启动时,onCreate()
方法就会调用
就如同我们在 Activity 中使用 HandlerThread 来完成耗时任务一样,在 IntentService 内部一样是将 HandlerThread 作为工作线程,因为 mServiceHandler 持有了 HandlerThread 的 Looper 对象,因此 mServiceHandler 就作为 IntentService 向 HandlerThread 发送 Message 的桥梁
@Overridepublic void onCreate() {super.onCreate();HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");//触发 HandlerThread 创建 Looper 对象thread.start();//获取 Looper 对象,以此来构建可以向子线程发送 Message 的 HandlermServiceLooper = thread.getLooper();mServiceHandler = new ServiceHandler(mServiceLooper);}
这里再看下 ServiceHandler 的类声明
当中,消息队列中的 Message 依次被传递给 handleMessage(Message)
方法进行处理,该方法是运行于子线程的,而耗时任务就交由抽象方法 onHandleIntent(Intent)
来完成,该方法需要交由子类来实现,并在该方法返回后调用 stopSelf(Int)
来停止 IntentService
可以看到,整个流程就是上面所说的
- IntentService 是以串行方式处理外部传递来的任务,即只有当上一个任务完成时,新的任务才会被执行
- 在处理完所有任务请求后会自动停止,因此不必手动调用
stopSelf()
方法
但要注意的是,调用 stopSelf(Int)
不一定会使 IntentService 停止,因为消息队列中可能还有未处理的消息,这个在后边会介绍
private final class ServiceHandler extends Handler {public ServiceHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {//将耗时任务转交给 onHandleIntent() 方法来完成//onHandleIntent() 需要由子类来实现onHandleIntent((Intent)msg.obj);//msg.arg1 即 startIdstopSelf(msg.arg1);}}
每次启动 IntentService 时,onStartCommand()
方法就会被调用,最后是通过 onStart()
方法向 mServiceHandler 发送 包含此次任务详情的 Message 对象
需要特别注意的是 startId 这个参数。每次回调 onStartCommand()
方法时,参数 startId 的值都是自动递增的,startId 用于唯一标识每次对 IntentService 发起的处理请求。如果 IntentService 同时处理多个 onStartCommand()
请求,则不应在处理完一个启动请求之后立即销毁 IntentService 。因为此时可能已经收到了新的启动请求,在第一个请求结束时停止服务会导致第二个请求被终止。为了避免这一问题,可以使用 stopSelf(int)
确保 IntentService 停止请求始终是基于最新一次的启动请求。也就是说,如果调用 stopSelf(int)
方法的参数值与 onStartCommand()
接受到的最新的 startId 值不相符的话,stopSelf(int)
方法就会失效,从而避免终止尚未处理的请求
@Overridepublic void onStart(@Nullable Intent intent, int startId) {Message msg = mServiceHandler.obtainMessage();msg.arg1 = startId;msg.obj = intent;mServiceHandler.sendMessage(msg);}//每次回调 onStartCommand() 方法时,参数 startId 的值都是自动递增的,startId 用于唯一标识每次对 Service 发起的处理请求//如果 Service 同时处理多个 onStartCommand() 请求,则不应在处理完一个启动请求之后立即销毁 Service//因为此时可能已经收到了新的启动请求,在第一个请求结束时停止服务会导致第二个请求被终止//为了避免这一问题,可以使用 stopSelf(int) 确保 Service 停止请求始终是基于最新一次的启动请求//也就是说,如果调用 stopSelf(int) 方法的参数值与 onStartCommand() 接受到的最新的 startId 值不相符的话,stopSelf(int) 方法就会失效,从而避免终止尚未处理的请求@Overridepublic int onStartCommand(@Nullable Intent intent, int flags, int startId) {onStart(intent, startId);return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;}
当中,onStartCommand()
方法的返回值可能由 setIntentRedelivery(boolean)
方法来决定
//用于决定 onStartCommand() 方法的返回值//如果参数为 true 则返回 START_REDELIVER_INTENT//如果系统在 onStartCommand() 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()//任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务//如果参数为 false 则返回 START_NOT_STICKY//如果系统在 onStartCommand() 返回后终止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务//这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务public void setIntentRedelivery(boolean enabled) {mRedelivery = enabled;}
还有其它几个相关方法
@Overridepublic void onDestroy() {//移除所有待发送的 MessagemServiceLooper.quit();}@Override@Nullablepublic IBinder onBind(Intent intent) {return null;}//此方法需要由子类来实现//在此方式中完成具体的耗时任务@WorkerThreadprotected abstract void onHandleIntent(@Nullable Intent intent);
IntentService 的源码理解起来不算难,不过这是建立在对 Android 系统中以 Handler、Looper、MessageQueue 组成的异步消息处理机制以及 HandlerThread 有所了解的基础上的,最后就再贴上对 IntentService 的全部源码注释
package android.app;import android.annotation.WorkerThread;
import android.annotation.Nullable;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;/*** 作者:叶应是叶* 时间:2018/6/22 13:39* 描述:https://github.com/leavesC* https://www.jianshu.com/u/9df45b87cfdf*/
public abstract class IntentService extends Service {private volatile Looper mServiceLooper;//关联了 mServiceLooper 的 Handlerprivate volatile ServiceHandler mServiceHandler;//IntentService 内部使用的子线程的线程名private String mName;//用于决定 onStartCommand() 方法的返回值private boolean mRedelivery;private final class ServiceHandler extends Handler {public ServiceHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {//将耗时任务转交给 onHandleIntent() 方法来完成//onHandleIntent() 需要由子类来实现onHandleIntent((Intent)msg.obj);//msg.arg1 即 startId//如果当前stopSelf(msg.arg1);}}//传入子线程名public IntentService(String name) {super();mName = name;}//用于决定 onStartCommand() 方法的返回值//如果参数为 true 则返回 START_REDELIVER_INTENT//如果系统在 onStartCommand() 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()//任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务//如果参数为 false 则返回 START_NOT_STICKY//如果系统在 onStartCommand() 返回后终止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务//这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务public void setIntentRedelivery(boolean enabled) {mRedelivery = enabled;}@Overridepublic void onCreate() {super.onCreate();HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");//触发 HandlerThread 创建 Looper 对象thread.start();//获取 Looper 对象,以此来构建可以向子线程发送 Message 的 HandlermServiceLooper = thread.getLooper();mServiceHandler = new ServiceHandler(mServiceLooper);}@Overridepublic void onStart(@Nullable Intent intent, int startId) {Message msg = mServiceHandler.obtainMessage();msg.arg1 = startId;msg.obj = intent;mServiceHandler.sendMessage(msg);}//每次回调 onStartCommand() 方法时,参数 startId 的值都是自动递增的,startId 用于唯一标识每次对 Service 发起的处理请求//如果 Service 同时处理多个 onStartCommand() 请求,则不应在处理完一个启动请求之后立即销毁 Service//因为此时可能已经收到了新的启动请求,在第一个请求结束时停止服务会导致第二个请求被终止//为了避免这一问题,可以使用 stopSelf(int) 确保 Service 停止请求始终是基于最新一次的启动请求//也就是说,如果调用 stopSelf(int) 方法的参数值与 onStartCommand() 接受到的最新的 startId 值不相符的话,stopSelf(int) 方法就会失效,从而避免终止尚未处理的请求@Overridepublic int onStartCommand(@Nullable Intent intent, int flags, int startId) {onStart(intent, startId);return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;}@Overridepublic void onDestroy() {//移除所有待发送的 MessagemServiceLooper.quit();}@Override@Nullablepublic IBinder onBind(Intent intent) {return null;}//此方法需要由子类来实现//在此方式中完成具体的耗时任务@WorkerThreadprotected abstract void onHandleIntent(@Nullable Intent intent);}
更多的源码解读请看这里:Java_Android_Learn
Android多线程之IntentService源码解析相关推荐
- Android多线程之ArrayBlockingQueue源码解析
阻塞队列系列 Android多线程之LinkedBlockingQueue源码解析 Android多线程之SynchronousQueue源码解析 Andorid多线程之DelayQueue源码分析 ...
- android handlerthread 线程管理,Android多线程之HandlerThread源码解析
一.概述 先来了解一下HandlerThread的几个特性 HandlerThread继续于Thread,本身就是一个线程类 HandlerThread在内部维护了自己的Looper对象,所以可以进行 ...
- Android 网络编程之OkHttp源码解析
前言:OkHttp框架是Android的网络请求框架,无数的项目都在使用着这个框架,重要性不言而喻; 本文会将OKHTTP的源码进行拆解,每个部分来单独学习,由简入深,循序渐进,篇幅较长,建议收藏,慢 ...
- Android 多线程之IntentService 完全详解
转载请注明出处(万分感谢!): http://blog.csdn.net/javazejian/article/details/52426425 出自[zejian的博客] 关联文章: Android ...
- HandlerThread和IntentService源码解析
简介 首先我们先来了解HandlerThread和IntentService是什么,以及为什么要将这两者放在一起分析. HandlerThread: HandlerThread 其实是Handler ...
- Handler消息机制(九):IntentService源码解析
作者:jtsky 链接:https://www.jianshu.com/p/0a150ec09a32 简介 首先我们先来了解HandlerThread和IntentService是什么,以及为什么要将 ...
- Android Glide 3.7.0 源码解析(八) , RecyclableBufferedInputStream 的 mark/reset 实现
个人博客传送门 一.mark / reset 的作用 Android Glide 3.7.0 源码解析(七) , 细说图形变换和解码有提到过RecyclableBufferedInputStream ...
- 【Android应用开发】EasyDialog 源码解析
示例源码下载 : http://download.csdn.net/detail/han1202012/9115227 EasyDialog 简介 : -- 作用 : 用于在界面进行一些介绍, 说明; ...
- 【Android 控件使用及源码解析】 GridView规则显示图片仿微信朋友圈发图片
今天闲下来想用心写一点东西,发现没什么可写的,就写一下最近项目上用到的一些东西吧.最近项目要求上传多图并且多图显示,而且要规则的显示,就像微信朋友圈的图片显示一样. 想了一下用GridView再适合不 ...
最新文章
- boost::contract模块实现courier信使的测试程序
- 【完全开源】微信客户端.NET版
- 使用MFC里的类的方法
- SAP UI5 jQuery.sap.includeStyleSheet
- 【转】【MySQL】事务与锁(四):行锁到底锁住的是什么?记录?字段?索引?
- vs2010+opencv3.4.1
- MATLAB实现三边定位
- 计算机网络实用技术教程txt,计算机网络实用技术教程
- UltraCompare文件内容比较工具
- android rom签名服务器,【精选】android_ROM分解定制签名教程.pdf
- 神经内分泌肿瘤如何分级,神经系统分级调节概念
- 1428C ABBB
- element-ui表格显示html格式
- 小米手机自带红外传感器安卓应用程序代码
- 动车报销凭证怎么取?高铁票的报销凭证在哪里取?
- 微信小程序:拼多多推客开发
- 小程序接入h5页面_h5页面和小程序交互
- html标签中文字换行
- 【国产之光】:龙芯1B(嵌入式方向)
- 【数字信号处理】卷积和乘法系列3之测不准原理