android 面试题 谈谈Handler

本文深入探讨了Android中Handler的作用、原理以及在面试中常被问到的问题。Handler主要用于异步消息处理,特别是在UI线程更新UI的限制及其原因。文章解释了为什么必须在UI线程更新UI,以及在特定情况下子线程可以更新UI的条件。同时,分析了在子线程中创建Handler的错误及解决办法,讨论了消息延迟的不可靠性,Message的obtain()与new Message()的区别,以及主线程Looper为何不会引发ANR。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Handler几乎每个人都会用,但是如果面试被问到,相信也不是每个人都能回答出来,特别是大点公司,特别喜欢问原理,或者你工作3年以上,别人不再问你怎么用了,而是问你底层实现原理,不然怎么区分你是高级还是中级 ,不给你定级别,工资不好给啊,

 

Handler作用:主要用于异步消息的处理,那么可能好奇,什么是异步消息,我们在学线程这块就知道,线程一般是用于处理比较耗时的操作,就是结果不是马上就能看的见的而是等时间过了才能看到结果的就是异步了,也就是说我在子线程中发送消息,然后要在主线程或者UI线程去更新UI的操作,

 

第一个问题:为什么android源码设计一定要在UI线程更新UI

比如我在子线程更新UI操作,那么有四个线程我去更新TextView控件上的文字,学会多线程的知道这可能会造成结果不是你想要的,多线程有三大特性:原子性。可见性   有序性  ,如果多个线程去操作TextView的话就涉及到线程安全问题了,如果是不安全就不能保证TextView显示的文字是你想要的结果,如果想让多线程变成安全的,那么就要加锁去控制,一旦加锁必然会影响性能,还可能导致过度绘制,这就是上面的答案.

第二个问题:难道子线程一定不能更新UI么?

针对上面的问题 我们写个代码:

 new Thread(){
            @Override
            public void run() {
                super.run();
                tv.setText("更新了");
            }
        }.start();

这个运行后是不会报错的,那我在线程中调用sleep()方法再试试


        new Thread(){
            @Override
            public void run() {
                SystemClock.sleep(3000);
                tv.setText("我在子线程中更新了");
            }
        }.start();

还是不会报错, 为什么,我相信网上很多博客  没有讲到这点,我们找到TextView的setText()方法:最终会调用TextView的这个方法:

 private void setText(CharSequence text, BufferType type,
                         boolean notifyBefore, int oldlen) {
  

而这个方法有一段代码:

 if (mLayout != null) {
            checkForRelayout();
        }

这个mLayout是不会为空,这个是Layout对象,它是封装了一些view的属性,在TextView中mLayout是可以获取到的:

 Layout layout =  tv.getLayout();

如果在onCreate()方法中去获取肯定为null,相当于你去onCreate ()方法获取view的高度或者宽度一样为0 ,那么就要走checkForRelayout()方法,

private void checkForRelayout() {
        // If we have a fixed width, we can just swap in a new text layout
        // if the text height stays the same or if the view height is fixed.

        if ((mLayoutParams.width != LayoutParams.WRAP_CONTENT
                || (mMaxWidthMode == mMinWidthMode && mMaxWidth == mMinWidth))
                && (mHint == null || mHintLayout != null)
                && (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight() > 0)) {
            // Static width, so try making a new text layout.

            int oldht = mLayout.getHeight();
            int want = mLayout.getWidth();
            int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth();

            /*
             * No need to bring the text into view, since the size is not
             * changing (unless we do the requestLayout(), in which case it
             * will happen at measure).
             */
            makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING,
                          mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(),
                          false);

            if (mEllipsize != TextUtils.TruncateAt.MARQUEE) {
                // In a fixed-height view, so use our new text layout.
                if (mLayoutParams.height != LayoutParams.WRAP_CONTENT
                        && mLayoutParams.height != LayoutParams.MATCH_PARENT) {
                    autoSizeText();
                    invalidate();
                    return;
                }

                // Dynamic height, but height has stayed the same,
                // so use our new text layout.
                if (mLayout.getHeight() == oldht
                        && (mHintLayout == null || mHintLayout.getHeight() == oldht)) {
                    autoSizeText();
                    invalidate();
                    return;
                }
            }

            // We lose: the height has changed and we have a dynamic height.
            // Request a new view layout using our new text layout.
            requestLayout();
            invalidate();
        } else {
            // Dynamic width, so we have no choice but to request a new
            // view layout with a new text layout.
            nullLayouts();
            requestLayout();
            invalidate();
        }
    }

你会看到不管if还是else都会调用:

 requestLayout();
 invalidate();

但是请注意上面有return语句,如果执行走到return语句就不回调用了,

if (mEllipsize != TextUtils.TruncateAt.MARQUEE) {
                // In a fixed-height view, so use our new text layout.
                if (mLayoutParams.height != LayoutParams.WRAP_CONTENT
                        && mLayoutParams.height !=
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值