美文网首页Handler
Handler相关的几个重要概念(面试)

Handler相关的几个重要概念(面试)

作者: ___瘦不了 | 来源:发表于2021-02-22 17:55 被阅读0次

    五个紧密相关的对象:

    Handler
    Message
    MessageQueue
    Looper
    Thread

    Handler

    postMessage -> MessageQueue.enqueMessage()
    sendMessage -> MessageQueue.enqueMessage()


    image.png

    最终都会调用enqueMessage()方法;

    dispatchMessage():

    image.png

    消息分发,最终会调用handleMessage()方法

    handleMessage():

    消息处理

    MessageQueue:

    优先级队列,实现方式-链表:根据时间进行排序的队列,队头的消息最早执行,队尾的消息最后执行

    enqueMessage():

    第一点:将msg.target赋值为发送消息的handler
    第二点:存储消息,把消息放到messageQueue中来,与消息队列中的消息进行时间对比,如果待插入的消息的执行时间先于其中的某一消息,则插入到该消息之前,如果sendMessage的delay的时间为0,则是立即执行。

    next()函数:

    从messageQueue中取消息,并以Message对象形式返回给调用者,
    如果消息队列中没有消息,nextPollTimeoutMillis会置为-1,当判断nextPollTimeoutMillis为-1时,会调用nativePollOnce函数等待
    nativePollOnce调用->linux->epoll机制->进行无线等待

    Looper:

    Looper.loop():

    死循环,线程在调用(或者Looper在调用)Looper.loop()函数,该函数中会调用MessageQueue中的.next()函数,并将取得的消息调用Handler的dispatchMessage方法,在dispatchMessage方法中最终会调用Handler的handleMessage方法

    关联面试问题:

    一个线程有几个Handler?

    无限个,可以随意创建

    一个线程有几个Looper?

    只有一个
    ####怎么保证只有一个Looper?
    ThreadLocal保证一个线程只有一个looper

    ThreadLocal:

    是一个<Key,Value>的map,在Looper.prepare()函数中会先进行判断是否存在looper的判断,如果不为空则抛出异常,如果为空则创建Looper并通过ThreadLocal.set()函数存放到ThreadLocal中。

    ThreadLocalMap:

    用于保存当前线程上下文,一个线程对应一个ThreadLocalMap

    线程和Looper怎么进行绑定?

    ThreadLocal.set():方法中首先获取了当前线程和当前线程对应的ThreadLocalMap两个对象,并将ThreadLocal对象作为key,Looper作为值存放到了ThreadLocalMap当中。ThreadLocalMap确定了一个ThreadLocal对应一个Looper,又根据上述一个线程对应一个ThreadLocalMap,确定了一个线程对应一个Looper

    Handler内存泄漏的原因?为什么其他内部类没有这个问题?

    匿名内部类持有外部类对象,Handler持有当前activity:
    由enqueMessage()函数的概念中的第一点得知,当handler发送消息时会将当前handler赋值给msg.target。得出如下逻辑:MessageQueue持有->Message->包含handler->handler持有activity

    为何在主线程中可以new Handler?在子线程中new Handler需要做什么准备?

    一、在开启app时zygote会fork一个进程 -> zygote会为该进程创建一个虚拟机 -> 会调用ActivityThread的main()函数 -> main函数中会调用Looper.prepareMainLooper()函数,初始化Looper。所以在主线程中可以随意new Handler,因为looper已经准备好了。
    二、在子线程中如果想要创建handler,需要先调用子线程的Looper.prepare()和looper.loop方法

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

    一、当MessageQueue.next()函数发现没有消息的时候,在next()函数中,会触发liunx层的epoll机制进入无限等待,直到再有新消息出现。在Looper.loop()函数中如果MessageQueue.next()返回的消息为null的时候会直接终止循环,跳出loop函数,直到下一次消息进入
    二、looper.quite(),1,调用MessageQueue的quite()方法,2,MessageQueue.quite()方法remove所有消息,3,不管有没有消息,唤醒线程,调用loop方法,当loop方法发现没有消息时会停止loop循环
    三、通过handler.getLooper()可以获取到子线程的looper,因此可以调用looper.quite方法。

    主线程需要释放吗?为什么?

    不需要,主线程调用quite函数时,会报异常。

    既然可以存在多个Handler往MessageQueue中添加数据(发消息时各个Handler可能处于不同线程),那它内部是如何确保线程安全的?

    一个线程->一个looper->一个messageQueue
    方法加锁synchronized,锁MessageQueue对象,防止在执行一个操作时其他线程对队列进行操作

    使用Message时该如何创建?

    handler.obtainMessage(),obtain采用享元设计模式,减少了Message对象的创建和销毁过程,降低了过多message时造成的内存抖动

    Looper死循环为什么不会导致应用卡死?

    产生ANR的问题不是因为主线程睡眠了,而是因为输入事件没有响应,输入事件没有响应他就没有办法唤醒这个looper,所以才加了5秒限制。消息队列是顺序执行,当执行到一个msg过于耗时时,导致阻断了messageQueue不能继续执行,所以才导致了ANR。

    looper中没有消息时loop函数会退出,唤醒线程的方法?

    1,looper中添加message,通过nativeWaite()->loop运作
    2,输入事件

    相关文章

      网友评论

        本文标题:Handler相关的几个重要概念(面试)

        本文链接:https://www.haomeiwen.com/subject/lfjkfltx.html