【Android】Handler机制和原理

一、作用

定义:消息传递过程中,收消息,与处理消息;

1、可以让对应的message和runnable在未来的某个时间进行相应处理;
2、让耗时操作放在子线程,让更新UI操作放在主线程;
3、队列就是依次执行,Handler会处理完一个消息或者执行完某个处理在进行下一步,不会出现多个线程同时要求进行UI处理而引发的混乱现象;

二、handler 中 sendMessage 和post 区别

sendMessage:
在工作线程中处理完耗时操作后调用handler的sendMessage(message)把message对象发送给主线程,在主线程中重写handlerMessage()方法,判断接收到的消息进行更新UI的操作;

Post:
post方法传递的是一个runnable对象,更新UI的操作也是在这个runnable的run方法中进行的,也就是说run方法中的代码是执行在主线程中的,虽然它是写在工作线程中,主线程在接收到消息后自动执行runnable的run方法中的代码。

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

由上源码可见,handler.post和handler.sendMessage本质上是没有区别的,都是发送一个消息到消息队列中,只不过post使用方式更简单。

三、sendMessage调用链

image.png

四、流程

image.png

1、Handler将Message发送到Looper的消息队列中(MessageQueue)
2、等待Looper的循环读取Message,处理Message,然后调用Message的target,即附属的Handler的dispatchMessage()方法,
3、将该消息回调到handleMessage()方法中,然后完成更新UI操作。

五、Handler引起的内存泄露及解决办法

泄漏的原因:
(普通的说法):handler发送的消息在当前handler的消息队列中,如果此时activity finish掉了,那么消息队列的消息依旧会由handler进行处理,若此时handler声明为内部类(非静态内部类),
内部类天然持有外部类的实例引用,这样在GC垃圾回收机制进行回收时发现这个Activity居然还有其他引用存在,因而就不会去回收这个Activity,进而导致activity泄露。

持有链说法:
ActivityThred持有Looper,
Looper持有MessageQueue=>>Message=>>Handler=>>Activity

image.png

六、内存泄漏解决方案:

1、把handler设置成静态内部类,因为静态内部类不持有外部类的引用,所以使用静态的handler不会导致activity的泄露

2、onDestroy生命周期中调用 handler.removeCallbacks();,进行释放

3、handler内部类持有外部activity的弱引用

七、MessageQueue是什么数据结构

MessageQueue内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表;

和传统的队列有点不一样,主要区别在于Android的这个队列中的消息是按照时间先后顺序来存储的,时间较早的消息,越靠近队头;

八、什么是消息同步屏障

同步屏障只在Looper死循环获取待处理消息时才会起作用,也就是说同步屏障在MessageQueue.next函数中发挥着作用。
在next()方法中,有一个屏障的概念(message.target ==null为屏障消息), 遇到target为null的Message,说明是同步屏障,循环遍历找出一条异步消息,然后处理。 在同步屏障没移除前,只会处理异步消息,处理完所有的异步消息后,就会处于堵塞 当出现屏障的时候,会滤过同步消息,而是直接获取其中的异步消息并返回, 就是这样来实现「异步消息优先执行」的功能 ;

怎么用
1、Handler构造方法中传入async参数,设置为true,使用此Handler添加的Message都是异步的;
2、创建Message对象时,直接调用setAsynchronous(true)
3、removeSyncBarrier() 移除同步屏障:

应用
在 View 更新时,draw、requestLayout、invalidate 等很多地方都调用了ViewRootImpl#scheduleTraversals(Android应用框架中为了更快的响应UI刷新事件在ViewRootImpl.scheduleTraversals中使用了同步屏障

九、Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

不阻塞的原因是epoll机制,他是linux里面的,在native层会有一个读取端和一个写入端,当有消息发送过来的时候会去唤醒读取端,然后进行消息发送与处理,没消息的时候是处于休眠状态,所以不回卡死

十、子线程能不能更新UI

可以

刷新UI, 都会调用到ViewRootImpl.Android每次刷新UI的时候,最终根布局ViewRootImpl.checkThread()来检验线程是否是View的创建线程。 ViewRootImpl创建的第一个地方,从Acitivity声明周期handleResumeActivity会被优先调用到,也就是说在OnResume后ViewRootImpl就被创建,这个时候无法在在子线程中访问UI了,上面子线程延迟了一会,handleResumeActivity已经被调用了,所以发生了崩溃 不延迟在creae里直接设置不会崩溃 线程更新UI也行,但是只能更新自己创建的View

1、子线程可以在ViewRootImpl还没有被创建之前更新UI;
2、访问UI是没有加对象锁的,在子线程环境下更新UI,会造成不可预期的风险;
3、开发者更新UI一定要在主线程进行操作;

同问:为什么Android系统不建议子线程访问UI?

在android中子线程可以有好多个,但是如果每个线程都可以对ui进行访问,我们的界面可能就会变得混乱不堪,这样多个线程操作同一资源就会造成线程安全问题。

需要解决线程安全问题的时候,我们第一想到的可能就是加锁,但是加锁会降低运行效率,所以android出于性能的考虑,并没有使用加锁来进行ui操作的控制。

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

推荐阅读更多精彩内容