美文网首页Android技术
Handler机制和原理

Handler机制和原理

作者: Qphine | 来源:发表于2022-04-20 23:59 被阅读0次

    一、作用

    定义:消息传递过程中,收消息,与处理消息;

    1、可以让对应的message和runnable在未来的某个时间进行相应处理;
    2、让耗时操作放在子线程,让更新UI操作放在主线程;
    3、队列就是依次执行,Handler会处理完一个消息或者执行完某个处理在进行下一步,不会出现多个线程同时要求进行UI处理而引发的混乱现象;

    二、handler 中 sendMessage 和post 区别

    sendMessage:
    在工作线程中处理完耗时操作后调用handler的sendMessage(message)把message对象发送给主线程,在主线程中重写handlerMessage()方法,判断接收到的消息进行更新UI的操作;

    Post:
    post方法传递的是一个runnable对象,更新UI的操作也是在这个runnable的run方法中进行的,也就是说run方法中的代码是执行在主线程中的,虽然它是写在工作线程中,主线程在接收到消息后自动执行runnable的run方法中的代码。

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    

    由上源码可见,handler.post和handler.sendMessage本质上是没有区别的,都是发送一个消息到消息队列中,只不过post使用方式更简单。

    三、sendMessage调用链

    image.png

    四、流程

    image.png

    1、Handler将Message发送到Looper的消息队列中(MessageQueue)
    2、等待Looper的循环读取Message,处理Message,然后调用Message的target,即附属的Handler的dispatchMessage()方法,
    3、将该消息回调到handleMessage()方法中,然后完成更新UI操作。

    五、Handler引起的内存泄露及解决办法

    泄漏的原因:
    (普通的说法):handler发送的消息在当前handler的消息队列中,如果此时activity finish掉了,那么消息队列的消息依旧会由handler进行处理,若此时handler声明为内部类(非静态内部类),
    内部类天然持有外部类的实例引用,这样在GC垃圾回收机制进行回收时发现这个Activity居然还有其他引用存在,因而就不会去回收这个Activity,进而导致activity泄露。

    持有链说法:
    ActivityThred持有Looper,
    Looper持有MessageQueue=>>Message=>>Handler=>>Activity

    image.png

    六、内存泄漏解决方案:

    1、把handler设置成静态内部类,因为静态内部类不持有外部类的引用,所以使用静态的handler不会导致activity的泄露

    2、onDestroy生命周期中调用 handler.removeCallbacks();,进行释放

    3、handler内部类持有外部activity的弱引用

    七、MessageQueue是什么数据结构

    MessageQueue内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表;

    和传统的队列有点不一样,主要区别在于Android的这个队列中的消息是按照时间先后顺序来存储的,时间较早的消息,越靠近队头;

    八、什么是消息同步屏障

    同步屏障只在Looper死循环获取待处理消息时才会起作用,也就是说同步屏障在MessageQueue.next函数中发挥着作用。
    在next()方法中,有一个屏障的概念(message.target ==null为屏障消息), 遇到target为null的Message,说明是同步屏障,循环遍历找出一条异步消息,然后处理。 在同步屏障没移除前,只会处理异步消息,处理完所有的异步消息后,就会处于堵塞 当出现屏障的时候,会滤过同步消息,而是直接获取其中的异步消息并返回, 就是这样来实现「异步消息优先执行」的功能 ;

    怎么用
    1、Handler构造方法中传入async参数,设置为true,使用此Handler添加的Message都是异步的;
    2、创建Message对象时,直接调用setAsynchronous(true) 3.removeSyncBarrier() 移除同步屏障:
    应用
    在 View 更新时,draw、requestLayout、invalidate 等很多地方都调用了ViewRootImpl#scheduleTraversals(Android应用框架中为了更快的响应UI刷新事件在ViewRootImpl.scheduleTraversals中使用了同步屏障

    九、Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

    不阻塞的原因是epoll机制,他是linux里面的,在native层会有一个读取端和一个写入端,当有消息发送过来的时候会去唤醒读取端,然后进行消息发送与处理,没消息的时候是处于休眠状态,所以不回卡死

    十、子线程能不能更新UI

    可以

    刷新UI, 都会调用到ViewRootImpl.Android每次刷新UI的时候,最终根布局ViewRootImpl.checkThread()来检验线程是否是View的创建线程。 ViewRootImpl创建的第一个地方,从Acitivity声明周期handleResumeActivity会被优先调用到,也就是说在OnResume后ViewRootImpl就被创建,这个时候无法在在子线程中访问UI了,上面子线程延迟了一会,handleResumeActivity已经被调用了,所以发生了崩溃 不延迟在creae里直接设置不会崩溃 线程更新UI也行,但是只能更新自己创建的View

    1、子线程可以在ViewRootImpl还没有被创建之前更新UI;
    2、访问UI是没有加对象锁的,在子线程环境下更新UI,会造成不可预期的风险;
    3、开发者更新UI一定要在主线程进行操作;

    同问:为什么Android系统不建议子线程访问UI?

    在android中子线程可以有好多个,但是如果每个线程都可以对ui进行访问,我们的界面可能就会变得混乱不堪,这样多个线程操作同一资源就会造成线程安全问题。

    需要解决线程安全问题的时候,我们第一想到的可能就是加锁,但是加锁会降低运行效率,所以android出于性能的考虑,并没有使用加锁来进行ui操作的控制。

    相关文章

      网友评论

        本文标题:Handler机制和原理

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