Handler(异步消息处理机制)所在的主线程里面既然是无限循环为什么不阻塞? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
nnegier

Handler(异步消息处理机制)所在的主线程里面既然是无限循环为什么不阻塞?

  •  
  •   nnegier 2022 年 4 月 22 日 14114 次点击
    这是一个创建于 1464 天前的主题,其中的信息可能已经有所发展或是发生改变。
    这个在成都面试感觉问到的有点多呀,所以发上来讨论一下。

    首先我个人没有直接回答这个问题,我个人觉得这个是会阻塞的,就是 ANR ,在一些方法里做了耗时操作(主线程),所以我觉得这个问题怪怪的。后面我也有补充,屏幕亮着,每隔一段时间 16ms (现在应该不是固定值了,看屏幕刷新率)就会刷新一次屏幕(当然还有很多其它类型的消息,触摸消息按键消息以及我们自己的消息等),意思就是一直都会有消息进来处理的意思。说到这儿,貌似已经不言自明了,这个问题就被放一边儿了,换个问题问了。

    只是这个问题真的是问题吗?都说问一个好问题很重要。这个感觉有些问题。
    21 条回复    2022-07-07 12:25:04 +08:00
    lait
        1
    lait  
       2022 年 4 月 22 日 via Android   2
    无限循环着代表 app 运行着,循环结束 app 就停止了呀。

    这个循环里如果有消息就处理,没有消息的时候就等待消息。

    等待消息的阻塞,和你在处理消息时耗时太长而引起 anr 不是一回事。
    你需要补充下基础概念,先看看 anr 的定义,然后再逐步深入理解等待消息的阻塞的基本解释和底层原理。
    nnegier
        2
    nnegier  
    OP
       2022 年 4 月 22 日
    @lait 对于你回复的第一行,如果循环结束,应用就是停止了。ActivityThread#main{ ... Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited");}。 可以好好看看源码。 关于剩下的解释我过会儿尝试解答下
    zagfai
        3
    zagfai  
       2022 年 4 月 22 日
    事件触发
    nnegier
        4
    nnegier  
    OP
       2022 年 4 月 23 日
    @lait 除开第一行(你第一行是错的),貌似和我的又有什么冲突呢,它没消息肯定就等待呀进入 sleep 状态不占用 CPU 时间片嘛,有消息了就唤醒嘛,所谓的消息驱动模式。你要是仔细看的话,我的意思就是这个。关于 anr ,我倒还没有关注过它的源码,但你可以看 https://developer.android.com/topic/performance/vitals/anr ,“如果 Android 应用的界面线程处于阻塞状态的时间过长,会触发“应用无响应”(ANR) 错误。”
    lait
        5
    lait  
       2022 年 4 月 23 日 via Android
    那您是对的,我理解有偏差吧。也许可以再一起研究研究 epoll 机制。
    GeruzoniAnsasu
        6
    GeruzoniAnsasu  
       2022 年 4 月 23 日   2
    1. 循环和阻塞根本就是两个维度的概念,循不循环跟一个调用阻塞不阻塞半毛钱关系都没有

    2. 我看不懂原问题想表达什么

    3. 世界上的 GUI ,基本原理全都是一样的,全都有一个处理事件的循环

    4. 世界上所有的程序,如果一个调用「阻塞了」,那么背后的原理也全都是一样的,caller 线程 hang 在了一个等待区,内核或调度器不会让 CPU 执行到「等待结束」后面的代码







    虽然我真的没法看懂你想讨论的原问题是什么,但下面这个问题:

    如果消息循环收不到任何消息,会阻塞在「取消息」操作上吗

    的答案是,取决于实现。从节省 cpu 时间的角度考虑完全可以让它阻塞住。


    下面这个追加问题:

    那么既然阻塞住了,为什么不会被检测出死循环 /无响应

    的答案是,调度器或者卡死检测器完全可以检测这个线程是否停在了等待区上,是则不认为线程无响应,这个线程 /routine 仍然是随时可调度的,只是没机会调度而已。
    卡死 /无响应的表现是这个线程超过了阈值时间仍然没有返回等待区。
    比如「取消息」就可以包含 renew 无响应超时的操作。假设它不阻塞,那么反正返回 caller 前刷新超时时间就好,如果它是阻塞的,那么执行流会回到调度器或内核里,调度器知道这个线程已经「重回可调度状态」,则不对其设置无响应定时器。
    如果调度器把执行流交给一个线程前先设置一个定时中断,等来的是中断触发而非线程主动交还执行流,说明被调度线程无响应 /超时了








    ps. 所有观点来自 OS 基本原理,我根本没研究过 androi 系统机制
    Cabana
        7
    Cabana  
       2022 年 4 月 23 日 via iPhone
    基础有点差,就去补补
    Cabana
        8
    Cabana  
       2022 年 4 月 23 日 via iPhone
    @Cabana “个人觉得”、“我觉得”,“应该不是”,“感觉有些问题”。搞技术的不要想当然,Android 从 java 到 native 源码都是公开,不懂就多看看。
    StrorageBox
        9
    StrorageBox  
       2022 年 4 月 23 日   1
    你是想说的 block 机制吧


    looper.loop()方法中开始循环调用 loopOnce()方法
    loopOnce()调用,messageQueue.next()方法,这方法是会被 block 的。

    - block 的原因呢
    有两种,其 1 是消息队列是空的,其 2 是开启了同步屏障,而消息队列中没有异步消息。

    - 解除 block 的原理也是这样,在 messageQueue.enqueueMessage() 中 1.添加新消息,2.添加异步消息(异步消息有两种添加方式,你可以从源码去了解一下)

    - 解除 block 的方法或者叫唤醒的方法,走 native wake(),本质上呢是通过 pipe 向指定 fd 进行写入一个 char(代码在 native Looper 中 ,同时可以了解一下 pipe 的原理)

    - 写入之后发生了什么,在 messageQueue.next()中首先调用 nativePollOnce()进入到 native Looper.poll_once(),进入到 native Looper.poll_inner() ,里面就能看到通过 epoll 去监听 fd 了(除了上面的指定 fd ,还含有多个 request 对应的 fd),如果有写入,就会有监听结果返回,Done 之后,block 便解除了。

    这个过程细节很多,感兴趣可以了解一下 epoll ,select/poll ,requst/response queue ,native handler 。

    结果就是,在 block 的时候,其实是有 epoll 去监听的。wake 之后就继续执行了。无限循环是为了保证线程不被结束,和 anr 是没有关系的。

    谈到 anr ,还是要重新理解一下概念。anr 发生的根本原因,不是线程什么都不做,而是 dispatchMessage()的执行时间,确切的说是上一个 message 的 disptch 时间过长导致现在的 message 不能被及时处理。

    我们看看 google 的定义"ANR 是一个问题,因为负责更新界面的应用主线程无法处理用户输入事件或绘制操作,引起用户的不满"。我觉得这个定义虽然没有把所有发生 anr 的情况罗列出来,但是很好的表达了 anr 的目的。

    回到你的描述上,为什么不卡屏幕刷新。作为应用程序的屏幕刷新时机有两种,1.requestlayout 刷新,2.重绘刷新,这两种刷新的原理都是通过 Choreographer 发送异步消息(在 Choreographer 就能看到两种异步消息的添加方式了)。

    纯手打,可能有些函数名有点出入。


    那么问题来了,屏幕上的图像到底是谁绘制的?
    nnegier
        10
    nnegier  
    OP
       2022 年 4 月 23 日
    @Cabana 我是发上来讨论,我也怕打脸呀
    nnegier
        11
    nnegier  
    OP
       2022 年 4 月 23 日
    @Cabana 我基础差?不知你对基础的定义是什么,好吧,反正我的确有被一些初级开发难倒过,他们喜欢问一些库怎么用,希望你不是。Android 源码的确是公开的,但那也太多了吧,你都看了吗,不笨蛋才会全看吧,那我们聚焦于 ANR 的具体实现吧,你能回答吗?我自己在看,可以给你聚焦一下,在 ActivityManagerService 里面能找到。另外你回复了两条,但什么都没回答,期待你可能的最新回复是有价值的
    Cabana
        12
    Cabana  
       2022 年 4 月 23 日 via iPhone
    @nnegier 这问题不是 Android 八股文么?一般问出来就是想看你有没有了解过过 epoll 机制。至于详细解释楼上都说的很详细了,你不听就没办法了。
    Cabana
        13
    Cabana  
       2022 年 4 月 23 日 via iPhone
    @Cabana 最后还是提醒你,搞混淆的一点:无限循环不是阻塞!这一点楼上的朋友也多次指出了。
    Cabana
        14
    Cabana  
       2022 年 4 月 23 日 via iPhone
    @nnegier 哦哦,我第一句话的表述可能不太清楚。有可能误会我在说你简单的基础知识差,这里我想表达的是是底层的基础知识,如有误会 sorry 。v 站本来就比较少人讨论 android 技术,希望不要给你留下不太好的印象。
    zpxshl
        15
    zpxshl  
       2022 年 4 月 23 日
    循环和阻塞完全是 2 个概念。
    unco020511
        16
    unco020511  
       2022 年 4 月 24 日
    gui 程序本来就是一个事件 /消息处理模型,没有消息时,相当于你的应用没有需要处理的界面信息,又何来阻塞一说呢?
    Geele
        17
    Geele  
       2022 年 4 月 24 日   1
    如果 Android 应用的界面线程处于阻塞状态的时间过长,会触发“应用无响应”(ANR) 错误。”

    4L 的这句话理解不准确,应该是一些指定的事件处理时间超过其阈值会触发 ANR
    nothingistrue
        18
    nothingistrue  
       2022 年 4 月 24 日
    线程阻塞状态,与同步任务的阻塞性,是两个概念。二者在某些情况下是相似的,但更可能是对立的。无线循环,是自己主动阻塞并让出资源,以便于整体上的不阻塞。

    再详细点我不想说,反正楼主也不是来问问题的。
    shidapi
        19
    shidapi  
       2022 年 4 月 24 日
    经典安卓面试题(狗头
    fromzero
        20
    fromzero  
       2022 年 4 月 26 日
    八股文 搜一下网上都是答案。-_-||
    verzqli
        21
    verzqli  
       2022 年 7 月 7 日
    ANR 和无限循环是两回事,ANR 是系统设置的,是为了提升用户体验产生的,如果说不为了用户体验,给 ANR 等待时间设个无限时间,那你的手机永远不会崩溃
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2728 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 53ms UTC 06:30 PVG 14:30 LAX 23:30 JFK 02:30
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86