android handler 异步,Android消息异步机制(ThreadLocal、MessageQueue、Looper、Handler关系总结)...

网上大神们关于Android消息异步机制的文章已经不少了,而且分析也很到位,我在这主要是为了记录我的体会,有错误的地方欢迎你们指正。

转载请注明:http://blog.csdn.net/Mr_immortalZ/article/details/51066869java

一.概述

Android的消息机制主要是指Handler的运行机制,Handler的运行须要底层的MessageQueue和Looper的支撑。

大体的运行过程以下图

58bebc72b6ab8eda1b8a6e7c0f0d3c4f.png

这里以UI线程(即主线程)与其余线程的交互为例。web

MessageQueue用于存储消息(Message)队列(内部存储数据结构是链表)数组

Looper会以无限循环的方式去查找是否有新的消息(Message),有则处理,没有则等待。数据结构

ThreadLocal能够在不一样线程中互不干扰地存储并提供数据,经过ThreadLocal能够轻松获取每一个线程的Looper。并发

Handler用于post消息(Message)给其余线程,并完成从其余线程到UI主线程的调转异步

综上可知 除了Handler运行在主线程,其余三个(MessageQueue,Looper,ThreadLocal运行在子主线程)

为了更好的理解运行时的关系。咱们对这四者分别介绍,最后再作总结。svg

ThreadLocal

由上图可知,Handler要想将消息投递给子线程,则必须投递到该子线程对应的Looper中。那么对于一个Handler来讲,我怎么知道,我投递的消息是否投递到了A线程,而不是B线程呢,换句话说,Handler又是怎么和我想要的Looper创建起联系的呢?答案就是ThreadLocal!也正是ThreadLocal的存在,让队列与线程关联上了!函数

ThreadLocal在线程中的做用oop

ThreadLocal是一个线程内部的数据存储类,经过它能够在指定的线程中存储数据,数据存储后,只有在指定的线程中才能够获取到该数据,其余线程没法获取(换而言之,当某些数据是以线程为做用域而且不一样线程具备不一样的数据副本时,就能够考虑使用ThreadLocal)。例如:对于Handler来讲,它须要获取当前线程的Looper,很显然Looper的做用域就是线程而且不一样线程具备不一样的Looper,这个时候经过ThreadLocal就可让Handler轻松获取到想要线程的Looper,从而进行其余操做。(之因此说轻松,是由于若是不采用ThreadLocal的方式来获取Looper,系统为了让Handler来获得想要的线程的Looper,则必须提供一个全局的哈希表供Handler查找指定线程的Looper,这样一来就必须提供一个相似LooperManager的类的管理,增长了使用成本)post

ThreadLocal其余场景下做用

ThreadLocal另外一个使用场景是复杂逻辑下的对象传递,好比监听器的传递,有些时候一个线程中的任务过于复杂,这可能表现为函数调用栈比较深以及代码入口的多样性,在这种状况下,咱们又须要监听器可以贯穿整个线程的执行过程,这个时候能够怎么作呢?其实就能够采用ThreadLocal,采用ThreadLocal可让监听器做为线程内的全局对象而存在,在线程内部只要经过get方法就能够获取到监听器。而若是不采用ThreadLocal,那么咱们能想到的多是以下两种方法:第一种方法是将监听器经过参数的形式在函数调用栈中进行传递,第二种方法就是将监听器做为静态变量供线程访问。上述这两种方法都是有局限性的。第一种方法的问题时当函数调用栈很深的时候,经过函数参数来传递监听器对象这几乎是不可接受的,这会让程序的设计看起来很糟糕。第二种方法是能够接受的,可是这种状态是不具备可扩充性的,好比若是同时有两个线程在执行,那么就须要提供两个静态的监听器对象,若是有10个线程在并发执行呢?提供10个静态的监听器对象?这显然是难以想象的,而采用ThreadLocal每一个监听器对象都在本身的线程内部存储,根据就不会有方法2的这种问题。

ThreadLocal之因此有这么奇妙的效果,是由于不一样线程访问同一个ThreadLocal的get方法,ThreadLocal内部会从各自的线程中取出一个数组,而后再从数组中根据当前ThreadLocal的索引去查找出对应的value值,很显然,不一样线程中的数组是不一样的,这就是为何经过ThreadLocal能够在不一样的线程中维护一套数据的副本而且彼此互不干扰。

MessageQueue

感受消息队列为啥好说的,主要操做就是增删。值得注意的就是:消息队列的内部实现并非真正的队列,而是用单链表(明显链表的增删操做易于队列)

fd67240496b4ae67ca88adad58d0bd1f.png

Looper

looper主要方法:

8f0e9d2591229a339ed91a2a302095c1.png

这里看看hongyang大神对Looper两个主要方法prepare(),loop()的分析(截图上是个人理解)

3e028b3607f95b01d294e02f079bb7ba.png

c7342f747688f5984e7f54d950eb26d4.png

Handler

消息的发送能够用过post或send一系列方法来实现,而post的一系列方法最终是经过send一系列方法来实现的。

public final boolean post(Runnable r)

{

return sendMessageDelayed(getPostMessage(r), 0);

}

Handler中分发消息的一些方法

post(Runnable)

postAtTime(Runnable,long)

postDelayed(Runnable long)

sendEmptyMessage(int what) //不须要自行new message,推荐

sendMessage(Message)

sendMessageAtTime(Message,long)

sendMessageDelayed(Message,long)

2b1d8350974ee6c06508888748309a73.png

e4b2b99766c242eaff29c86edd9fceea.png

由上可知,Handler发送消息仅仅是想消息队列插入了一条消息。MessQueue的next方法会返回这条消息给Looper,Looper通过取消息将取得的消息交给Handler处理,即

public void dispatchMessage(Message msg) {

if (msg.callback != null) { //在post消息中,咱们进行了new Runnable操

handleCallback(msg); //做,走这个分支

} else { //直接在建立Handle的构造函数中就进行了new Callback操做

if (mCallback != null) {

if (mCallback.handleMessage(msg)) {

return;

}

}

handleMessage(msg);

}

}

可见咱们在建立Handler实例时有两种方法

2ab3a276e5feaed72c71681b79d29ad6.png

未完待更新

异步编排是指在复杂的处理流程中,使用一些异步处理的手段来提高效率和性能。在这种场景下,ThreadLocal可能会出现获取失败的问题。ThreadLocal主要是提供了保持对象的方法和避免参数传递的方便的对象访问方式,而不是用来解决对象共享访问问题。每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确地访问到自己的对象。通过将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,可以在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。异步编排和ThreadLocal在处理复杂的流程时可以结合使用,通过ThreadLocal来保存和访问各个线程的对象,从而实现异步处理的目的。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [ThreadLocal跨线程问题](https://download.csdn.net/download/weixin_38669881/13750353)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [ThreadLocal和同步异步的解释](https://blog.csdn.net/doom20082004/article/details/53612339)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [异步 多线程 线程池 异步编排 ThreadLocal的使用](https://blog.csdn.net/weixin_45552441/article/details/125605532)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值