简介

首先我们先来了解HandlerThread和IntentService是什么,以及为什么要将这两者放在一起分析。 HandlerThread:

HandlerThread 其实是Handler + Thread + Looper的组合,它本质上是一个Thread,因为它继承了Thread。相比普通的Thread,它不会堵塞,因为他内部通过Looper实现了消息循环机制,保证了多个任务的串行执行。缺点是效率比较低,因为,串行执行比起并行执行,效率肯定会比较较低。

IntentService:

IntentService是继承并处理异步请求的一个类,其本质上是一个Service,因为他继承了Service,所以开启IntentService和普通的Service一致。但是他和普通的IntentService不同之处在于,他可以处理异步任务,在任务处理完成之后会自动结束Service。另外我们可以启动IntentService多次,而每一个耗时任务会已工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且是串行执行的。

好了在了解HandlerThread和IntentService分别是什么之后,我们来解决第二个问题,那就是为什么我要将2者放在一起分析?其实IntentService的内部是通过HandlerThread和Handler来实现异步操作的,当我们了解了HandlerThread的使用和原理之后,再去理解IntentService就会容易的多。好的,下面让我们开始HandlerThread的源码之旅。

#HandlerThread的使用和原理 ###HandlerThread的使用 这里我们要实现一个每隔5s更新TextView中的值的一个demo,源码如下:

public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";private TextView mTv;private Button mBtn;HandlerThread mHandlerThread;Handler mThreadHandler;@Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();mBtn.setOnClickListener(new View.OnClickListener() {@Override public void onClick(View v) {initThread();}});}private void initView() {mTv = (TextView) findViewById(R.id.tv);mBtn = (Button) findViewById(R.id.btn);}private void initThread() {//创建一个HandlerThread并开启线程mHandlerThread = new HandlerThread("update-msg");mHandlerThread.start();//从mHandlerThread中得到Looper并创建HandlermThreadHandler = new Handler(mHandlerThread.getLooper()) {@Override public void handleMessage(Message msg) {Log.v(TAG, "currentThread===>" + Thread.currentThread() + "   what====>" + msg.what);try {update();} catch (Exception e) {e.printStackTrace();}mThreadHandler.sendEmptyMessage(200);}};mThreadHandler.sendEmptyMessage(200);}private void update() throws Exception {Thread.sleep(3000);runOnUiThread(new Runnable() {@Override public void run() {String result = "每隔3s更新一次:";result += Math.random();mTv.setText(result);}});}
}复制代码

输出的日志如下:

currentThread===>Thread[update-msg,5,main] what====>200

从日志我们可以看出handleMessage运行在我们创建的HandlerThread("update-msg")之下。我们有理由怀疑这跟我们传入的mHandlerThread.getLooper()有关。我们的mThreadHandler 是在UI线程中创建的,按理来说handleMessage应该运行在UI线程中才对。了解Handler原理的都知道,handleMessage方法是在Handler的dispatchMessage方法中被调用的且dispatchMessage方法没有进行线程切换。所以线程切换应该发生在dispatchMessage被调用的地方,那dispatchMessage是在哪被调用的呢?我们发现Loop的loop()方法中调用了dispatchMessage()方法。(这里我就不贴Handler和Loop相关的代码,感兴趣的可以参考我以前的文章:Handler的原理)而且我们还发现了loop()方法的注释如下:

意思是loop()方法运行在Loop被绑定的线程中。

那Loop又是在什么时候被绑定的呢?

就是这2个方法对Loop进行了绑定。那这个sThreadLocal又是什么鬼?它到底有什么用?别急,我们去看下它创建的地方: 它其实就是一个ThreadLocal,关于ThreadLocal的原理,大家可以参考:ThreadLocal源码深入分析 在这里我简单的说下,其实ThreadLocal的作用,就是通过Thread中的threadLocals:ThreadLocalMap变量将我们通过ThreadLocal#set方法传进来的数据跟Thread进行绑定,从而保证了访问到的变量属于当前线程,每个线程都保存有一个变量副本,每个线程的变量都不同,而同一个线程在任何时候访问这个本地变量的结果都是一致的。当此线程结束生命周期时,所有的线程本地实例都会被GC。ThreadLocal相当于提供了一种线程隔离,将变量与线程相绑定。ThreadLocal通常定义为private static类型

通过上面的分析,我们已经知道了Handler#handleMessage方法会运行在Loop说绑定的线程上,验证了我们一开始的猜想。这里Loop是从我们创建的HandlerThread中得到的,而HandlerThread其实就是一个线程,所以Loop绑定在了新创建的HandlerThread上。但是我们并不满足于此,我们得进一步看看HandlerThread和普通的Thread到底有什么不一样。

HandlerThread的源码解析

HandlerThread的代码量其实并不多,它继承于Thread,主要的方法其实就是run方法

@Overridepublic void run() {mTid = Process.myTid();//创建Loop并绑定当前线程Looper.prepare();//关键代码synchronized (this) {mLooper = Looper.myLooper();notifyAll();}//设置线程优先级Process.setThreadPriority(mPriority);onLooperPrepared();Looper.loop();mTid = -1;}public Looper getLooper() {if (!isAlive()) {return null;}// If the thread has been started, wait until the looper has been created.synchronized (this) {while (isAlive() && mLooper == null) {try {wait();} catch (InterruptedException e) {}}}return mLooper;}
复制代码

短短的几行代码确几乎实现了我们想要的所有功能,我们来看关键代码run方法中的synchronized 代码块其实对应于getLooper方法中的synchronized代码块,这样做的目的是为了确保,我们通过getLoop()方法得到的Loop对象一定是被初始化后的Loop。当Loop被初始化以后会调用抽象方法onLooperPrepared(),他一般被用于在开启队列循环之前做一些初始化的操作,然后执行任务队列。

总结

HandlerThread的原理已经分析完了,我们来总结一下它的特点:

1.HandlerThread它就是一个线程,和开启普通的线程得到操作一致

2.HandlerThread需要搭配Handler使用,单独使用的意义不大

3.HandlerThread会将通过handleMessage传递进来的任务进行串行执行,这是由messageQueue的特性决定的,从而也说明了HandlerThread效率相比并行操作会比较低

IntentService的使用和原理

分析完HandlerThread之后我们来分析一下IntentService的使用和原理,老规矩我们先看怎么使用。

IntentService的使用

public class MyIntentService extends IntentService {private static final String TAG = "MyIntentService";public MyIntentService() {super("MyIntentService");Log.v(TAG,"MyIntentService===>MyIntentService()" + "  currentThread==>" + Thread.currentThread());}/*** Creates an IntentService.  Invoked by your subclass's constructor.** @param name Used to name the worker thread, important only for debugging.*/public MyIntentService(String name) {super(name);Log.v(TAG,"MyIntentService===>MyIntentService(name)" + "  currentThread==>" + Thread.currentThread());}@Override public void onCreate() {super.onCreate();Log.v(TAG, "MyIntentService===>onCreate" + "  currentThread==>" + Thread.currentThread());}@Override protected void onHandleIntent(@Nullable Intent intent) {Log.v(TAG, "MyIntentService===>onHandleIntent" + "  currentThread==>" + Thread.currentThread());try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}}@Override public int onStartCommand(@Nullable Intent intent, int flags, int startId) {Log.v(TAG, "MyIntentService===>onStartCommand" + "  currentThread==>" + Thread.currentThread());return super.onStartCommand(intent, flags, startId);}
}
复制代码

调用服务和我们普通的Service一致 输出的日志如下

MyIntentService===>MyIntentService() currentThread==>Thread[main,5,main] MyIntentService===>onCreate currentThread==>Thread[main,5,main] MyIntentService===>onStartCommand currentThread==>Thread[main,5,main] MyIntentService===>onHandleIntent currentThread==>Thread[IntentService[MyIntentService],5,main]

从中我们可以看出onHandleIntent方法是运行在子线程中的,更有意思的是,当我们在onHandleIntent 方法中执行延迟操作时,打印的日志如下描述: 1、当服务没执行完时又点击了开启服务的操作,此时,onStartCommand方法会立即执行,而onHandleIntent方法会在上一个任务执行完以后再去执行onHandleIntent方法。 2、当服务已经执行完被自动结束以后,再去调用service,输出的日志和第一次输出的日志一致。

可能我说的比较抽象,大家自取去操作一遍就会发现我所说的有意思的地方。从上面的日志输出,我们可以得出以下结论: 1、IntentService在任务执行完以后会自动结束 2、IntentService接收的任务是串行执行的,并且互不干扰 3、IntentService的生命周期和普通的Service一致,只不过多了一个onHandleIntent回调方法,并且它是串行回调的,等待上一个任务执行完以后才会再次被调用

但是为什么会这样呢?大家有没有想过。当然,所有的答案都隐藏在源码里,让我们一起去揭开他神秘的面纱吧。

IntentService源码解析

首先我们先来看下IntentService的几个成员变量,如下图所示:

关于Loop和Handler我们都很熟悉了,前者是遍历消息队列的消息泵后者则是处理Handler发送过来的消息的。下面我来看下他们初始化得到地方。 Loop初始化

原来他们都是在Service的onCreate回调方法中被初始化的。 通过上文HandlerThread的分析,我们知道ServiceHandler的handleMessage方法会运行在mServiceLooper绑定的指定线程上。这里这也就验证了我们上文日志的输出。

下面我们来解决另外一个问题,也就是IntentService的生命周期函数的执行情况。 请看下面的代码:

我们都知道当服务被启动以后,再次调用服务的时候都会回调onStartCommand方法,onStartCommand又调用了onStart方法,而onStart方法中只是通过Handler发送一个异步消息,然后ServiceHandler的handleMessage收到消息以后调用了onHandleIntent,这也就验证了上文的日志输出。

下面我们来重点分析一下Service的stopSelf()方法,他有两个重载方法,一个有参,一个无参,那他们之间有什么不同呢? 我们还是通过源码来看一下吧。

可以看到无参方法只是简单的调用了有参方法,并传入了一个-1的参数。所以我们只有直接分析有参的方法就可以。 由于Android sdk并没有开放ActivityManageProxy(我们知道ActivityManage在客户端得到代理是ActivityManageProxy)的代码,所以我们只能通过查找相关资料来解决我们的疑惑。 最终我在官网上得到的答案如下:

简单来说就是stopSelf中的startId对应于onStartCommand中的startId,当stopSelf(startId)中的startId等于onStartCommand中的最后一个进来的startId的时候,就代表消息队列中没有更多的消息需要处理了,所以执行完当前的消息以后,会去执行Service的stop操作

总结

关于IntentService的分析到这就告一段落了,其实IntentService就是基于HandlerThread机制来实现的,它允许我们在onHandleIntent回调方法中执行异步操作。同时要注意他的生命周期回调函数的差异。下面贴上官网上关于IntentService类的介绍,帮助大家理解。

HandlerThread和IntentService源码解析相关推荐

  1. Handler消息机制(九):IntentService源码解析

    作者:jtsky 链接:https://www.jianshu.com/p/0a150ec09a32 简介 首先我们先来了解HandlerThread和IntentService是什么,以及为什么要将 ...

  2. Android多线程之IntentService源码解析

    想要了解 IntentService 的工作原理需要先对 Android 系统中以 Handler.Looper.MessageQueue 组成的异步消息处理机制以及 HandlerThread 有所 ...

  3. AsyncTask、HandlerThread、IntentSerivce源码解析

    在进行耗时操作时,一般new Thread().start();开启一个子线程,然后通过handler消息去更新ui(关于handler可以看这里:android Handler.Looper.Mes ...

  4. Android - Intentservice源码解析

    https://blog.csdn.net/javazejian/article/details/52426425 转载于:https://www.cnblogs.com/qlky/p/1067562 ...

  5. Handler全家桶之 —— Handler 源码解析

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 本文首发于本人简书 前言 好记性不如烂笔头. 这是一个系列文章,将会包括: Handler全家桶之 -- Handler 源码解析 ...

  6. android handlerthread 线程管理,Android多线程之HandlerThread源码解析

    一.概述 先来了解一下HandlerThread的几个特性 HandlerThread继续于Thread,本身就是一个线程类 HandlerThread在内部维护了自己的Looper对象,所以可以进行 ...

  7. android intent 源码,Android 基础之 IntentService 源码

    Android 基础之 IntentService 源码 Android,IntentService,源码 IntentService 位于 android.app 包下面,是 Service 的一个 ...

  8. Android面试题Service,Android面试题-IntentService源码分析

    自定义控件 联网 工具 数据库 源码分析相关面试题 Activity相关面试题 Service相关面试题 与XMPP相关面试题 与性能优化相关面试题 与登录相关面试题 与开发相关面试题 与人事相关面试 ...

  9. Handler源码解析2

    Handler源码解析1 https://blog.csdn.net/qq_44076155/article/details/110676740 Handler源码解析2 https://blog.c ...

最新文章

  1. offset/client/scroll一些总结
  2. C# 文件/文件夹压缩解压缩
  3. 数学图形(2.7)sphere sine wave
  4. 在Eclipse上安装插件springsource-tool-suite详解,完美不报错!以及yml无自动提示?
  5. 计算机教学辅助在教学中的应用研究,云计算辅助教学在高职计算机教学中的应用研究.doc...
  6. python将非0数视为false_python 面试题
  7. Kubernetes学习之路(四)之Node节点二进制部署
  8. python apriori算法 sklearn_R 和 Python (numpy scipy pandas) 用于统计学分析,哪个更好?...
  9. java 保存的代码怎么写_java实现写入并保存txt文件的示例代码
  10. 国家计算机二级access考试试题,全国计算机二级access试题及答案
  11. ACL2021 | 把关键词生成看成集合预测问题
  12. bzoj4771 七彩树
  13. 2018ISMAR Comparing Different Augmented Reality Support Applications for Cooperative Repair...
  14. jTessBoxEditor训练3500常用汉字
  15. 线性代数(第六版) 同济大学数学系 编 高等教育出版社 课后习题答案
  16. Camera IIC总线
  17. Unfinished
  18. python读取页眉页脚,python批量替换页眉页脚
  19. P4 开发实践 — Overview
  20. 机器学习----决策树(一)

热门文章

  1. 百度编辑器ueditor每次编辑后多一个空行的解决办法
  2. IOS开发 使用CGContextRef绘制文字时的设置
  3. XP系统的用户头像是在那个文件夹里面
  4. 深度有趣 | 06 变分自编码器
  5. 转大神的中国剩余定理
  6. CoordinatorLayout、AppBarLayout、Toolbar使用详解
  7. 洛谷——P2035 iCow
  8. 微服务架构的基础框架选择:Spring Cloud还是Dubbo?
  9. inline-block 间距
  10. 从零开始学_JavaScript_系列(14)——dojo(7)(饼图,BorderContainer,hashchange,弹窗)...