Android Handler 原理分析

Android Handler 的原理分析

Handler 是安卓中最常用的组件。作用就是 线程间的消息通知 但是 Java的jdk 明明有很多但是为什么要有这个呢 1 几乎看不到多线程死锁 2 规范线程的使用 早期移动设备内存很低

首先要明白handler 的使用 1 线程a 创建一个handler 接受的地方 然后线程b 引用线程A 的handler 进行一个消息的发送

然后是多线程的思想

我有ABDCE 几个线程。然后进行 在A 中创建5个Handler 然后在其他几个线程中分别在 任务完成之后 像A 中持有的handler 进行消息的发送 这样看来 本质上 其实 就是内存共享的方式。和我们自己写的监听回调是不是差不多的 都是持有同一个对象 然后进行回调 但是 其他线程是不允许进行ui 会不安全 所有才设计了这个 我感觉最大的作用
就是用于和主线程进行通信 从而进行ui 的更新 安全问题。
在 A 线程启动的时候 都会进行looper 的创建 同时进行了MessageQueue 也就是消息队列的创建。所以说 我们的一个线程可以创建很多个handler 然后每个handler 这个线程会维护一个
属于自己的looper 和自己的消息队列。我们在其他线程进行 handler的send 或者post 本质上 都是调用的MessageQueue 的入队列方法。所以说 我有多个线程 每个线程都创建一个handler 那么其实我是有多个looper 和多个MessageQueue 的 大家自己用自己的。
looper 是线程自己调用的。在主线程中 我们不需要自己调用 因为activityTheard 会帮我们调用 在调用的时候也是先调用looper.prepare 在调用looper.loop 不然一样会报错

Handler :


image.png

最后都会执行到Handler.enquueMessage() ->MessageQueue.enquueMessage() 入队列

theard --------->Looper.loop -------->MessageQueue.next() 出队列 -》handler.dispatchMessage

所以源码重点分析 在looper MessageQueue 的消息的入队列 和 出队列中干了些什么事情。

首先是在looper.prepare 的时候创建了我们线程自己的looper 同时创建了属于自己的MessageQueue 然后将其保存在线程的副本 TheardLocal中


image.png

Looper.loop 中 首先是获取当前线程 的looper 并且 获取属于自己的MessageQueue
然后开启死循环 不断的 从 自己的MessageQueue队列中 Message msg = queue.next(); 取出来然后进行消息的分发


image.png

MessageQueue 中的enqueueMessage 方法中 是入队列的逻辑 这个函数会存在多线程 同时操作的情况 那么他就必须保证 自己的原子和防止重排序 所有 需要加锁
所以在这个函数里面 用了Synchronized 关键字 那么 同样的 在取出来的时候 next() 方法也是需要加锁的 因为你不能一边取一边存

image.png

我们创建入队列的时候 会给每一个消息 添加当前的时间戳 来进行标记 所以在加入的时候 是根据时间来进行优先级排序的 最早执行的会在前面
那么问题来了在next()函数中 首先是一个死循环 如果说执行到了还不到执行的时间任务 会将其当前的时间加上需要等待的时间 然后交给nativepollOnce 来进行阻塞的等待
如何进行阻塞的呢。是在进入循环之前有一个变量mPendingIdHandlers 的值为-1 如果是我的message 需要进行等待 会执行 这个函数 小于《=0 然后进行continue 同时mBlocked =true
然后再次进入死循环 然后在取消息之前 就会进入 naitivePollOnce 来进行阻塞 等待
所以在looper.loop 方法在Message msg = queue.next(); 这句话就会一直卡主 1 没有message msg=null 2 被阻塞了 msg=null
然后会执行 msg==null 然后return 掉 这样主线程就去做其他事情了 就不会阻塞掉 而主线程会永远执行 只要我们系统在运行 所以会一直挂起


image.png

image.png

然后是阻塞了 我们如何唤醒呢 因为 我们前面说了2种情况 一直是消息不到执行的时间 一种是 没有消息 消息队列为空 那么我们这个时候插入一个消息 如果队列
为空 那么是不是可以拿到消息进行执行了呢。如果不到时间 那么我们插入一个马上可以执行的消息 是不是也能够执行了呢。
但是都是需要唤醒
在源码里面我们发现 Synchronized 的判断

if(p==null||when==0||when<p.when){
needweak =mBlocked
}

其实就是2种情况

1 消息头为空其实就是我们的空队列 那么这个时候
2 如果当前最近的消息头的时间 比当前时间要大 那么就执行唤醒策略
在最下面会有 needweak=true 会执行唤醒策略

设计模式

handler 的dispatchMessage(Message msg){
if(msg.callbakc!=null){
handlercallbakc(msg)
}else{
if(mCallback!=null){
if(mcallback.handleMessage(msg)){
return
}
}
handleMessage(msg)
}
}

这个其实是一种责任链模式

还有在我们消息 处理了之后 或者说消息销毁的时候 会讲所有的消息 都存放到一个回收池中
那么sPool
不管是我们自己创建new 的message 还是 obtain()调用的message 都是会回到
sPool回收池中 这样不断的进行循环利用message 对象 减少内存的开销 和 尽量内存分配连续的区域 减少gc的发生
这个模式叫做享元模式

这个就是 目前对于Handler机制的了解 作为自己的一个学习记录吧。
来波面试题
1Handler是什么?
2消息机制是什么?
3为什么不能在子线程中访问UI?
4在子线程中创建Handler报错是为什么?
5如何在子线程创建Looper?
6 为什么通过Handler能实现线程的切换?
答:当在A线程中创建handler的时候,同时创建了MessageQueue与Looper,Looper在A线程中调用loop进入一个无限的for循环从MessageQueue中取消息,当B线程调用handler发送一个message的时候,会通过msg.target.dispatchMessage(msg);将message插入到handler对应的MessageQueue中,Looper发现有message插入到MessageQueue中,便取出message执行相应的逻辑,因为Looper.loop()是在A线程中启动的,所以则回到了A线程,达到了从B线程切换到A线程的目的
7 Handler.post的逻辑在哪个线程执行的,是由Looper所在线程还是Handler所在线程决定的?
答 是在Looper.loop()方法中,从MsgQueue中拿出msg,并且执行其逻辑,这是在Looper中执行的,因此有Looper所在线程决定。
8 Looper和Handler可以处于一个线程吗?子线程中可以用MainLooper去创建Handler吗?
答 1 可以 。
2 子线程中Handler handler = new Handler(Looper.getMainLooper());,此时两者就不在一个线程中。
9 Handler的post方法发送的是同步消息吗?可以发送异步消息吗?
答 1 用户层面发送的都是同步消息
2 不能发送异步消息
3 异步消息只能由系统发送。

10 Handler的post()和postDelayed()方法的异同?
答 底层都是调用的sendMessageDelayed() post()传入的时间参数为0 postDelayed()传入的时间参数是需要的时间间隔。
11 MessageQueue.next()会因为发现了延迟消息,而进行阻塞。那么为什么后面加入的非延迟消息没有被阻塞呢?
12 Looper.quit/quitSafely的本质是什么?
本质都是调用MsgQueue的quit() 只是前者会清空所有消息 而后者只会清楚延时消息 吧剩余的非延时的消息分发出去 都不会在接受新消息进来了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271