欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

一天一面试题---第一天(Handler)

程序员文章站 2022-05-06 18:48:03
...

问题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更新.

面试题第二天