腾讯面试挂了---面试官:说说Android中Looper在主线程中死循环为什么没有导致界面的卡死?

这位朋友直接蒙了,后面的面试就结束的很快,虽然被告知“回去等通知”,但是从面试官的态度很明显可以看出来:基本上没戏了。

正文

其实这个问题并不难回答。关键点如下:

  • 导致卡死的是在Ui线程中执行耗时操作导致界面出现掉帧,甚至ANR,Looper.loop()这个操作本身不会导致这个情况。
  • 有人可能会说,我在点击事件中设置死循环会导致界面卡死,同样都是死循环,不都一样的吗?Looper会在没有消息的时候阻塞当前线程,释放CPU资源,等到有消息到来的时候,再唤醒主线程。
  • App进程中是需要死循环的,如果循环结束的话,App进程就结束了。

当然,如果想要回答的更好,建议阅读下文。

这是以前回答过的一个相似的问题,原文奉上。

题目的详细描述:

app程序入口中为主线程准备好了消息队列

而根据Looper.loop()源码可知里面是一个死循环在遍历消息队列取消息

而且并也没看见哪里有相关代码为这个死循环准备了一个新线程去运转,但是主线程却并不会因为Looper.loop()中的这个死循环卡死,为什么呢?

就像Activity的生命周期这些方法这些都是在主线程里执行的,那这些生命周期方法是怎么实现在死循环体外能够执行起来的?

详细答案

要完全彻底理解这个问题,需要准备以下4方面的知识:Process/Thread,Android Binder IPC,Handler/Looper/MessageQueue消息机制,Linux pipe/epoll机制。

总结一下,这个问题有3个疑惑:

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

2.没看见哪里有相关代码为这个死循环准备了一个新线程去运转?

3.Activity的生命周期这些方法这些都是在主线程里执行的吧,那这些生命周期方法是怎么实现在死循环体外能够执行起来的?

针对这些疑惑,更进一步详细地解答:

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

这里涉及线程,先说说说进程/线程,**进程:**每个app运行时前首先创建一个进程,该进程是由Zygote fork出来的,用于承载App上运行的各种Activity/Service等组件。进程对于上层应用来说是完全透明的,这也是google有意为之,让App程序都是运行在Android Runtime。大多数情况一个App就运行在一个进程中,除非在AndroidManifest.xml中配置Android:process属性,或通过native代码fork进程。

线程:线程对应用来说非常常见,比如每次new Thread().start都会创建一个新的线程。该线程与App所在进程之间资源共享,从Linux角度来说进程与线程除了是否共享资源外,并没有本质的区别,都是一个task_struct结构体,在CPU看来进程或线程无非就是一段可执行的代码,CPU采用CFS调度算法,保证每个task都尽可能公平的享有CPU时间片

有了这么准备,再说说死循环问题:

对于线程既然是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程,我们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢?**简单做法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出,**例如,binder线程也是采用死循环的方法,通过循环方式不同与Binder驱动进行读写操作,当然并非简单地死循环,无消息时会休眠。但这里可能又引发了另一个问题,既然是死循环又如何去处理其他事务呢?通过创建新线程的方式。

真正会卡死主线程的操作是在回调方法onCreate/onStart/onResume等操作时间过长,会导致掉帧,甚至发生ANR,looper.loop本身不会导致应用卡死。

(2) 没看见哪里有相关代码为这个死循环准备了一个新线程去运转?

事实上,会在进入死循环之前便创建了新binder线程,在代码ActivityThread.main()中:

public static void main(String[] args) {

//创建Looper和MessageQueue对象,用于处理主线程的消息
Looper.prepareMainLooper();

//创建ActivityThread对象
ActivityThread thread = new ActivityThread();

//建立Binder通道 (创建新线程)
thread.attach(false);

Looper.loop(); //消息循环运行
throw new RuntimeException(“Main thread loop unexpectedly exited”);
}

thread.attach(false);便会创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程,具体过程可查看 startService流程分析,这里不展开说,简单说Binder用于进程间通信,采用C/S架构。关于binder感兴趣的朋友,可查看我回答的另一个知乎问题:
为什么Android要采用Binder作为IPC机制? - Gityuan的回答

另外,ActivityThread实际上并非线程,不像HandlerThread类,ActivityThread并没有真正继承Thread类,只是往往运行在主线程,该人以线程的感觉,其实承载ActivityThread的主线程就是由Zygote fork而创建的进程。

主线程的死循环一直运行是不是特别消耗CPU资源呢? 其实不然,这里就涉及到Linux pipe/e****poll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,详情见Android消息机制1-Handler(Java层),此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

写在最后

很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从哪里入手去学习,对此我整理了一些资料

如果你熟练掌握以下列出的知识点,相信将会大大增加你通过前两轮技术面试的几率!这些内容都供大家参考,互相学习。

①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包,最后觉得有帮助、有需要的朋友可以点个赞

-B7zvZoJ3-1712034492961)]

[外链图片转存中…(img-OIVt72Cu-1712034492962)]

[外链图片转存中…(img-bElbOMWb-1712034492962)]

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 21
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 的 Looper 机制是一个消息循环系统,用于管理应用程序的消息队列。在 Python ,我们可以使用 `queue` 模块来实现类似的机制。以下是一个使用 `queue` 模块实现的死循环示例: ```python import queue def my_handler(): while True: try: message = my_queue.get(block=True, timeout=1) except queue.Empty: continue # 处理消息 print(f"Received message: {message}") # 将消息放回队列 my_queue.put(message) # 创建消息队列 my_queue = queue.Queue() # 启动消息处理循环 my_handler() ``` 在这个示例,我们定义了一个 `my_handler()` 函数,它包含一个无限循环,它会从一个队列获取消息,并对消息进行处理。如果队列为空,循环将等待 1 秒钟,然后重试。在处理完消息后,循环将消息放回队列,以便其他线程可以处理它。 要使用这个循环,我们需要创建一个队列,并将消息放入队列。例如: ```python # 将两条消息放入队列 my_queue.put("Hello") my_queue.put("World") # 等待一段时间 time.sleep(5) # 取出队列的消息 while not my_queue.empty(): message = my_queue.get() print(f"Got message: {message}") ``` 在这个示例,我们创建了一个队列,并将两条消息放入队列。然后,我们等待 5 秒钟,并从队列取出所有的消息并打印它们。在此过程,`my_handler()` 函数将一直运行,并处理队列的消息,直到程序结束。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值