一天一面试题---第一天(Handler)
问题1:在子线程中一定不能进行UI更新?
答:不一定,因为当我们在setText方法时,会走一个
checkForRelayout()方法,
而在这个方法里面最后都会走 requestLayout();invalidate();这二个方法而在requestLayout()方法里面走一个
ViewRootImpl.requestLayout()方法, 在这里会对线程进行检测,checkThread();,mThread肯定是是主线程,如果在子线程中则Thread.currentThread()肯定不等于main所以抛异常
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
如果invalidate();这个方法优化于checkThread()则不会异常
问题2:Handler为什么会内存泄漏,解决的方法
答:因为当Handler发送一个消息出去的时候,已经将消息存入了消息队列MessageQueue里面,而在Looper的looper方法会无限循环的向消息队列里取消息,当一个Activity销毁时,发送出去的消息并没有销毁.面Handler是一个内部类,内部类会执有外部类引用,也就是Activity.最后都会执行Handler.handlerMeaager方法.
解决方法:1:弱引用Activity;
2:onDestory里将handler设置为null;
问题3:handler.post方法为什么是执行在主线程
答:当handler.post一个Runnable方法时,会将Runnable封装在Message里的一个callback里,最后放入MeaageQueue里面,而Looper.looper取消息的时候是在主线程,取到消息最后走的是Handler.dispatchMessage方法,里面会判断message.callback是否为空,不为空则会走Runnable的run方法
问题4:在子线程中能直接创建Handler对象?
答:不能,因为为子线程中直接创建Handler对象时会执行
Looper.myLooper();方法获取Looper,如果Looper为空时就会抛异常.
解决方法: 在创建handler的时候先执行
Looper.prepare();
源码分析
Handler主要是用来在子线程到主线程之间进行切换.
当我们启动APP的时候,最开始走的是ActivityThread类里的main方法,在这里面开始会执行Looper.prepareMainlooper方法,里会会实例化一个主线程Looper对像,并保存在ThreadLocal成员变量里,而在new Looper的时候,则又会在构造方法里实例化一个MessageQueue方法里,所以在进入APP的时候,就会实例经主线程的Looper和MessageQueue而且一定是唯一的.最后然后走Looper.loop方法进行轮询取消息.
当我们创建Handler的时候,会将主线程里的Looper 和MessgeQueue赋值给其成员变量,当sendMessage的时候,不管是发送什么样的消息,最终都走的是enqueueMeaage方法就是准备将消息压入MessageQueue里面去,当然在这之中会将handler传给Message,所以message会执有Handler引用.
最后在Looper.loop轮询到方法的时候,就会拿到message的target也就是Handler,会调用到其dispatchMessage方法中对消息进行分发,因为loop()这个方法是在主线程的,所以dispatchMessage分发的消息也是在主线程的执行的,最最后就走到了我们的handlerMessage方法里面,就可以在主线程的进行UI更新.
上一篇: scrapy爬虫实例
下一篇: 一天一个我不会的前端面试题10.28