Handler面试那些事

Posted 我想月薪过万

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Handler面试那些事相关的知识,希望对你有一定的参考价值。

1、子线程到主线程通信都有哪些方式?子线程到主线程通信的原理?

这个是 android 特有的概念。线程间的通信方式:eventbus、rxjava、livedata。然而,这些方式的底层原理都是基于 handler,所以,下面我将为大家讲解 handler 通信原理。

handler调度流程:

子线程: handler.sendMessage(msg) =》 handler.enqueueMessage =》MessageQueue.enqueueMessage()
主线程:Looper.loop=》queue.next()=》handler.handleMessage()

handler的核心原理图:

两个重要类的图解: ​

2、handler内存泄漏的原因是什么? 

JVM 垃圾回收机制:GCroot 回收机制

持有链: static sThreadLocal -》 mLooper -》MessageQueue -》msg -》handler -》 activity

解决方法:打破持有链

3、子线程中如何创建handler?

  • 写法一:直接在子线程创建 handler,错误
new Thread(
                () -> 
                    Handler mHandler = new Handler(new Handler.Callback() 
                        @Override
                        public boolean handleMessage(@NonNull Message msg) 
                            return false;
                        
                    );

                    mHandler.sendMessage(new Message());
                
).start();

报错展示:

Process: com.wust.empty02, PID: 25002
    java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()
        at android.os.Handler.<init>(Handler.java:207)
        at android.os.Handler.<init>(Handler.java:133)
        at com.wust.empty02.MainActivity.lambda$onCreate$0$MainActivity(MainActivity.java:20)
        at com.wust.empty02.MainActivity$$ExternalSyntheticLambda0.run(Unknown Source:2)
        at java.lang.Thread.run(Thread.java:919)

报错源码位置:

  • 写法二:正确写法
new Thread(
                () -> 
                    //创建该线程的 looper
                    Looper.prepare();
                    //创建该线程的 handler
                    Handler mHandler = new Handler(new Handler.Callback() 
                        @Override
                        public boolean handleMessage(@NonNull Message msg) 
                            return false;
                        
                    );
                    //looper开启循环
                    Looper.loop();
                    
                
).start();

 这种写法有一个缺点:这样的话 该线程就只能创建一个handler。

  • 写法三:通过  public Handler(@NonNull Looper looper) 该构造方法进行 handler 创建
package com.wust.empty02;


import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity 

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        MyHandlerThread t = new MyHandlerThread();
        t.start();
        try 
            //这句话如果去掉 就会导致 t.getmLooper() 为空,因为你无法保证 Handler handler = new Handler(t.getmLooper()); 在 t 线程 run 之后执行
            Thread.sleep(1000*3);
         catch (InterruptedException e) 
            e.printStackTrace();
        
        Handler handler = new Handler(t.getmLooper());
    


class MyHandlerThread extends Thread 
    private Looper mLooper;

    @Override
    public void run() 
        Looper.prepare();
        this.mLooper = Looper.myLooper();
        Looper.loop();
    

    public Looper getmLooper() 
        return mLooper;
    

从上面代码的注释 就可以很明显的看出其缺点之所在了。无法保证 t.getmLooper() 在 线程 run 方法之后执行。

  • 方法四:使用 HandlerThread 创建线程
HandlerThread t = new HandlerThread("wustyq");
t.start();
Handler handler = new Handler(t.getLooper());

系统的这个 HandlerThread 中的 run 方法为什么就能保证在 t.getLooper() 之前执行呢??关键代码 + 关键思想如下:

为什么要用  synchronized ? 为了解决并发操作 =》 即 run 和  getLooper 只能同时执行一个。

为什么要用  notifyAll() ?因为有可能多个线程调用了wait()方法进行等待。

为什么要用 while?不用 if?因为这个 wait() 可能由别的线程 notify() 唤醒。所以要使用while反复检测。

4、子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?主线程呢?

quit:loop退出。防止内存泄漏。不会被停止。

5、handler如何处理发送延时消息?

会调用底层 Linux =》 epoll 完成等待

6、我们使用Message时应该如何创建它?

obtain()

以上是关于Handler面试那些事的主要内容,如果未能解决你的问题,请参考以下文章

Android 面试中常问到的那些 Handler 面试题

面试那些事

聊聊面试官那些事。。分享我的个人看法,认同的顶一顶。。。

程序员面试之前要准备的那些事

[oldboy-django][4python面试]有关yield那些事

『文末送书』java面试那些事