面试常问Handler 的问题合集

2024-09-02 20:48

本文主要是介绍面试常问Handler 的问题合集,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

问:Looper.prepare做了什么操作?

检查了当前线程是否已经有了Looper对象,如果已经有了Looper对象,那么就直接报错。保证每个线程只有一个Looper.

    private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));}

他是利用ThreadLocal 实现的,保证每个线程只有一个Looper.

问:Looper、Handler、 MessageQeue 怎么关联起来的?

当调用了Looper.prepare 的时候,会创建一个Looper.在Looper 的构造方法里面,会创建一个MessageQeue. Looper 和 MessageQeue 就关联起来了。
当我们调用Looper.Loop 的时候,looper 就会不停的从MessageQeue 里面拿消息。

我们发送消息的时候,需要创建一个Handler, 创建Handler的时候,会获取当前线程的Looper,同时拿到MessageQeue,我们就可以使用当我们发送一个消息的时候,Message 里有一个成员变量target,会拿着当前Handler的引用。这样当Looper 获取到Message 的时候,就会调用target(Handler) 的 dispatchMessage 方法。

这样Looper Handler MessageQeue 就关联起来了。

问:怎么保证一个线程只有一个handler?

呵呵呵,给的问题都是错的。一个线程里面可以有多个handler,但是只有一个looper 和一个MessageQeue.
因为Looper 的构造方法是私有的,所以只有 prepare 方法才可以new Looper,但是这个方法只能调用一次,因为第二次调用会抛异常。原因是Looper 这个类里面有个static final 的变量,叫做sthreadLocal,调用prepare 的时候,会调用threadLocal.get(),如果返回值不为空,就会抛异常。
多个handler 无所谓啊,因为用handler 发送消息,msg 里面有一个target 指向发消息的handler,处理消息的时候,会调用msg.target disapatch msg.

问:MessageQeue内部怎么实现的消息队列?先有一个延迟消息,后来有个直接发的消息,为什么后来直接发的消息先执行?

想想也知道,肯定要排序啊。我的天。感觉handler 还有很多细节,没有看。native 的也没有看。MessageQeue 是链表实现的, 因为发送的消息可能被插入到任何位置,应对随机插入数据,最好的数据结构就是链表。消息插入的时候,会根据deley time 进行排序插入,所以,就算是先发了一个延迟消息,又发送了立刻执行的消息,先执行的是后面立即执行的消息。

问:MessageQeue 的数据结构是什么?

是链表,不是队列。

问:如果我修改系统时间,会影响sendEmptyMessageDelayed 吗?

不会,因为延迟是基于开机时间,而不是时钟。

    public final boolean sendMessageDelayed(Message msg, long delayMillis){if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}

问:Handler sendMessage 和 postRunnable 有什么区别?

    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;}

发送的时候,没有什么区别。只不过postRunnable 是会自动创建一个Message,而sendMessage 需要你去创建。

    /*** Handle system messages here.*/public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}

Looper 取到消息后,会调用Handler 的dispatchMessage 处理消息,处理消息的时候,会判断callback是否是空,不是空直接执行callback.否则调用handleMessage方法处理消息。

问:为什么不同的线程,调用Loop.myLooper()就可以得到当前自己线程的Looper 对象? 而不是所有线程获取的是同一个Looper?

因为Looper.myLooper 返回值是从ThreadLocal 里面获取的,不同的线程有不同的值。

问:为什么到了时间就处理消息?是有定时吗?

问:为什么message.obtain 可以获取message对象? 可以用到之前回收的message对象?

因为message 里面里有私有的静态变量,Message,保存了当前的回收了的message的头结点。因为是静态的,所以所有的message 公用一个message 静态变量。

问:Nativepollonce 为什么要使用c层去写?java 层不能完全实现吗?

因为java 层不好控制休眠。因为looper 是个死循环,当消息队列没有消息的时候,就会阻塞,释放cpu。(sleep 也可以啊。)当有消息的时候,会唤醒程序运行。

问:android looper 为什么不会阻塞主线程?

android 程序的主入口在activityThread 类的main 方法。里面做了几个简单的事情,随后启动了looper. 如果不启动looper 那么程序就会直接退出了。所以looper 不但不会导致阻塞,反而没有looper机制会导致程序结束。所有的其他的操作,比如16ms 刷新一帧,都是通过hanlder 的handlermessage 去完成的。所以整个android 就是在handler 里面完成所有的onResume,onPause,而程序不会退出。

主线程的死循环一直运行是不是特别消耗CPU资源呢?

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

当休眠的时候,send 进来的message 的延迟如果是0,那么都会唤醒这个线程。

    boolean enqueueMessage(Message msg, long when) {synchronized (this) {msg.markInUse();msg.when = when;Message p = mMessages;boolean needWake;//如果p = null 说明是新的一个消息//或者是一个立马执行的消息//或者发送的消息 小于当前头消息的时间if (p == null || when == 0 || when < p.when) {// New head, wake up the event queue if blocked.msg.next = p;mMessages = msg;needWake = mBlocked;} else {//非实时的//如果消息是异步的  那么可以去唤醒// 为什么p.target 要是空的呢?needWake = mBlocked && p.target == null && msg.isAsynchronous();Message prev;for (;;) {prev = p;p = p.next;if (p == null || when < p.when) {break;}//如果在这之前有异步的消息 那么就不用唤醒了if (needWake && p.isAsynchronous()) {needWake = false;}}msg.next = p; // invariant: p == prev.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true;}

handler 怎么实现的延迟多少秒去执行消息?

有一个队列,当发现下一个消息是延迟消息,那么调用nativePullOnce 的时候,传的第二个参数,就是下次Poll的时间,就是你的延迟时间。nativePollOnce(ptr, nextPollTimeoutMillis); 如果是-1表示一直阻塞下去等待消息。0表示完全不阻塞,就算没有线程去写入信息到文件操作符,也不会阻塞。只有当nextPollTimeoutMillis 为一个大于0 的数,他才会去阻塞,等待有线程写入消息。

如果MessageQueue 里面没有消息的话, 那么nativePollOnce 的第二个参数就是-1,表示一直休眠下去,等待下一个消息发进来。

如果在休眠期间,发送了一个消息就会里面调用nativeWake 唤醒线程。

如果我发送了一个延迟5s 的消息,那么nativePollOnce 会等待5s,这时候有一个线程发送了延迟3s 的消息,那么就会立马唤醒线程去读取消息。如果发现是延迟3s,就会继续调用nativepollOnce timeOut 参数设置为3s.

Message obtain () 怎么回收,怎么重复使用?

public static Message obtain() {synchronized (sPoolSync) {if (sPool != null) {Message m = sPool;sPool = m.next;m.next = null;m.flags = 0; // clear in-use flagsPoolSize--;return m;}}return new Message();
}

什么时候回收?

            msg.recycleUnchecked();

Looper.loop 方法里面的,当一条消息处理完之后,就会回收。

用静态的一个链表保存消息,处理完一个消息,就会回收,
如果你没有源码,可能看不到引用recycleUnchecked的地方。其实在Looper.loop 里面调用了。

setAsynchronous 的作用

    public void setAsynchronous(boolean async) {if (async) {flags |= FLAG_ASYNCHRONOUS;} else {flags &= ~FLAG_ASYNCHRONOUS;}}

Asynchronous的消息是必须和屏障一块使用的,当有屏障的时候,屏障后面的普通消息不会执行,只会执行异步消息。可以参考这篇文章:

https://www.cnblogs.com/wytiger/p/13031145.html

这篇关于面试常问Handler 的问题合集的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1131035

相关文章

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

SpringBoot启动报错的11个高频问题排查与解决终极指南

《SpringBoot启动报错的11个高频问题排查与解决终极指南》这篇文章主要为大家详细介绍了SpringBoot启动报错的11个高频问题的排查与解决,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一... 目录1. 依赖冲突:NoSuchMethodError 的终极解法2. Bean注入失败:No qu

MySQL新增字段后Java实体未更新的潜在问题与解决方案

《MySQL新增字段后Java实体未更新的潜在问题与解决方案》在Java+MySQL的开发中,我们通常使用ORM框架来映射数据库表与Java对象,但有时候,数据库表结构变更(如新增字段)后,开发人员可... 目录引言1. 问题背景:数据库与 Java 实体不同步1.1 常见场景1.2 示例代码2. 不同操作

如何解决mysql出现Incorrect string value for column ‘表项‘ at row 1错误问题

《如何解决mysql出现Incorrectstringvalueforcolumn‘表项‘atrow1错误问题》:本文主要介绍如何解决mysql出现Incorrectstringv... 目录mysql出现Incorrect string value for column ‘表项‘ at row 1错误报错

如何解决Spring MVC中响应乱码问题

《如何解决SpringMVC中响应乱码问题》:本文主要介绍如何解决SpringMVC中响应乱码问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Spring MVC最新响应中乱码解决方式以前的解决办法这是比较通用的一种方法总结Spring MVC最新响应中乱码解

pip无法安装osgeo失败的问题解决

《pip无法安装osgeo失败的问题解决》本文主要介绍了pip无法安装osgeo失败的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 进入官方提供的扩展包下载网站寻找版本适配的whl文件注意:要选择cp(python版本)和你py

解决Java中基于GeoTools的Shapefile读取乱码的问题

《解决Java中基于GeoTools的Shapefile读取乱码的问题》本文主要讨论了在使用Java编程语言进行地理信息数据解析时遇到的Shapefile属性信息乱码问题,以及根据不同的编码设置进行属... 目录前言1、Shapefile属性字段编码的情况:一、Shp文件常见的字符集编码1、System编码

Spring MVC使用视图解析的问题解读

《SpringMVC使用视图解析的问题解读》:本文主要介绍SpringMVC使用视图解析的问题解读,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Spring MVC使用视图解析1. 会使用视图解析的情况2. 不会使用视图解析的情况总结Spring MVC使用视图

Redis解决缓存击穿问题的两种方法

《Redis解决缓存击穿问题的两种方法》缓存击穿问题也叫热点Key问题,就是⼀个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击,本文给大家介绍了Re... 目录引言解决办法互斥锁(强一致,性能差)逻辑过期(高可用,性能优)设计逻辑过期时间引言缓存击穿:给

Java程序运行时出现乱码问题的排查与解决方法

《Java程序运行时出现乱码问题的排查与解决方法》本文主要介绍了Java程序运行时出现乱码问题的排查与解决方法,包括检查Java源文件编码、检查编译时的编码设置、检查运行时的编码设置、检查命令提示符的... 目录一、检查 Java 源文件编码二、检查编译时的编码设置三、检查运行时的编码设置四、检查命令提示符