Handler源码分析

Handler是一套消息处理机制,可以通过它发送消息,并将所有的消息在指定的线程统一处理。

Handler是什么?

Handler是一套消息处理机制,可以通过它发送消息,并将所有的消息在指定的线程统一处理。

为什么用Handler?

Android是单线程UI模型,在其他线程更新UI会报CalledFromWrongThreadException异常,所以当我们在子线程跟新UI时,需要先创建一个主线程的Handler,向此handler发送跟新UI的消息,然后就可以在主线程处理更新UI操作。

Handler怎么使用?

1.子线程向UI线程发消息

public static void main(String[] args) {
    UIHandler uiHandler = new UIHandler();
    OtherThread otherThread = new OtherThread(uiHandler);
    otherThread.start();
    otherThread.sendMessage();
}

static class UIHandler extends Handler {
    public UIHandler() {
        super(Looper.getMainLooper());
    }

    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
    }
}

static class OtherThread extends Thread {
    private final Handler handler;

    public OtherThread(Handler handler) {
        this.handler = handler;
    }

    public void sendMessage() {
        Message message = Message.obtain();
        handler.sendMessage(message);
    }
}

2.UI线程向子线程发消息

public static void main(String[] args) {
    OtherThread otherThread = new OtherThread();
    otherThread.start();
    otherThread.sendMessage();
}

static class OtherThread extends Thread implements Handler.Callback{
    private Handler handler;

    @Override
    public void run() {
        super.run();
        Looper.prepare();
        //创建Handler之前一定要先调用Looper.prepare()创建Looper,否则会报异常
        //开发中可以直接使用HandlerThread来实现,HandlerThread里面和这里一样对Looper做了封装
        handler = new Handler(this);
        Looper.loop();
    }

    @Override
    public boolean handleMessage(@NonNull Message msg) {
        return false;
    }

    public void sendMessage(){
        Message message = Message.obtain();
        handler.sendMessage(message);
    }
}

Handler源码分析

handle源码分析

上图是Handler消息机制的简要流程示意图,主要包含三个类:

  • Handler: 发送消息到MessageQueue,以及处理消息。
  • Looper: 内部持有一个MessageQueue,通过loop()方法不断从MessageQueue中取出Message交由对应的信息处理器(Handler)处理,线程切换也是在这里完成。
  • MessageQueue: 消息队列,其实是维护了一个根据消息【执行时间先后】连接起来的单向链表结构,当没有消息时会阻塞。

Handler 是标准的事件驱动模型,简单说就是Handler发送消息到MessageQueue,Looper从MessageQueue取出消息下发到对应的Handler处理。

下面直接通过代码分析,看一下具体实现细节。

在通过Handler发送消息前需要先调用Looper.prepare()Looper.loop()方法来创建并启动Looper,不然会报异常。

主线程的Looper不需要我们创建是因为在Android程序的入口已经调用了Looper.prepareMainLooper()及loop()方法。

//ActivityThread.java  
public static void main(String[] args) {  
    ...  
    Looper.prepareMainLooper();  
    ... 
    Looper.loop();  
    throw new RuntimeException("Main thread loop unexpectedly exited");  
} 

下面先看一下Looper.prepare()方法:

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

该方法创建一个Looper保存到ThreadLocal里面,Looper和Thread是一对一绑定关系。在创建Looper时会初始化一个MessageQueue消息队列,MessageQueue和Looper也是一对一绑定关系。该方法一个线程只能调用一次,否则报上面的异常。

然后看一下Looper.loop()方法:

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;
    ...
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        ...
        msg.target.dispatchMessage(msg);
        ...
        //循环复用
        msg.recycleUnchecked();
    }
}
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

先从ThreadLocal中获取当前线程的Looper,拿到MessageQueue,然后开启一个无限循环,不断调用MessageQueue的next()方法获取下一个消息,然后分发到Message中的Handler中,最后把Message回收服用。

下面看一下MessageQueue的next()方法

Message next() {
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }
    ...

    for (;;) {
        ...
        
        //nextPollTimeoutMillis>0时阻塞线程,nextPollTimeoutMillis==-1时一直阻塞
        nativePollOnce(ptr, nextPollTimeoutMillis);

        // 尝试取出下一个消息返回
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            
            //处理同步障碍,同步障碍就是msg.target==null的消息
            //当消息队列有同步障碍,同步消息下发受到阻碍,异步消息不受影响
            //阻塞消息通过postSyncBarrier()发送,直到通过调用removeSyncBarrier()来释放指定的障碍
            if (msg != null && msg.target == null) {
                //将同步的消息跳过,直到下一个异步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            
            if (msg != null) {
                if (now < msg.when) {
                    // 消息还没到触发时间,设置要休眠的时间
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        //将下一个消息链接到被阻塞的最后一个同步消息上
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }

            //处理IdleHandler,IdleHandler是Handler提供的一种在消息队列空闲时执行任务的机制,即空闲任务
            // IdleHandler只有当MessageQueue处于空闲(MessageQueue为空或者第一个要发送的消息没到触发时间)时才会执行
            if (pendingIdleHandlerCount < 0
                && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            // mIdleHandlers里没有要执行的IdleHandler,跳过下面的处理IdleHandler逻辑,继续下次循环
            if (pendingIdleHandlerCount <= 0) {
                //下次消息需要阻塞线程,所以标识当有新消息时需要被唤醒
                mBlocked = true;
                continue;
            }
            //把mIdleHandlers保存到mPendingIdleHandlers,后面for循环会删除mIdleHandlers中的IdleHandler
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        // 执行空闲任务
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler

            boolean keep = false;
            try {
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }

            // keep返回false,执行一次即从mIdleHandlers中移除
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
        //空闲任务每次获取消息只执行一次,所以将pendingIdleHandlerCount值为0,下一次for循环就不会再执行IdleHandler的逻辑.
        pendingIdleHandlerCount = 0;

        // 当IdleHandler执行完,之前没到触发时间的消息可能已经可以执行,
        // 所以等待时间置为0,直接看一下有没有消息需要发送
        nextPollTimeoutMillis = 0;
    }
}

这个方法用来获取下一个Message。首先开启一个无限循环,然后进入同步锁获取下一个消息,如果消息此刻还没有到时间,设置一下阻塞时间nextPollTimeoutMillis,进入下次循环的时候会调用nativePollOnce(ptr, nextPollTimeoutMillis)进行阻塞;如果消息此刻到时间了,返回msg。

Looper准备好了,下面我们用Handler发送消息:

调用Handler的sendMessage()方法,最终会调用enqueueMessage()方法。

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {
    msg.target = this;
    ...  
    return queue.enqueueMessage(msg, uptimeMillis);
}

这个方法先将handler保存到Message中,然后调用MessageQueue的enqueueMessage()方法。

下面看一下MessageQueue的enqueueMessage()方法:

boolean enqueueMessage(Message msg, long when) {
    ...

    synchronized (this) {
       ...

        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            //插入队列的头部
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // 插入队列中间,通常我们不必唤醒事件队列,除非队列头部有障碍物,并且消息是队列中最早的异步消息。
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            //插入位置的前一个消息
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                //时间小于下个消息时间时跳出循环
                if (p == null || when < p.when) {
                    break;
                }
                //异步消息不在头部,不需要唤醒
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            //将消息加入单向链表
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

这个方法主要是将消息根据时间排序将消息插入到MessageQueue中,根据是否需要唤醒调用nativeWake()方法。如果调用唤醒方法MessageQueue的next()方法将不再阻塞,Looper将继续循环从MessageQueue中获取消息交由对应的Handler处理。到这Handler消息机制Java部分就完成了。

线程之间切换

分析了这么多,那Handler是怎么实现线程切换的呢?从头到尾也没看到切换线程的代码啊😂。

通过上面的分析我们知道,当我们在A线程调用Handler发送消息,在B线程调用Looper.loop(),不断从MessageQueue中取出消息分发到Handler,所以消息处理是在B线程,这就完成了消息线程的切换。

pipe/epoll机制

类比Java中的PipedInputStream和PipedOutputStream,用于线程间通信,当缓冲区没有数据时,输入流所在的线程将处于阻塞状态,输出流向缓冲区写入一个数据时,输入流所在的线程将解除阻塞。

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

Looper通过MesssageQueue的next()方法获取到下一个消息,当取不到消息时,会调用nativePollOnce(ptr,-1)释放CPU资源进入休眠状态;直到下次发送新消息激活nativeWake()方法,才会唤醒for循环继续获取消息。native层是使用了Linux内核的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。而造成ANR是因为在单次循环中做了耗时操作,其他消息得不到及时处理导致的。

MessageQueue如何管理消息?

MessageQueue中的消息都是按照消息触发时间顺序由近及远进行的排序。
插入消息时,首先找到比发送消息触发时间小的最后一个消息,将靠前消息的next赋值成当前的消息,当前消息的next赋值靠前消息的next。

取出消息时,从队列取第一个消息作为待发送的消息,如果该消息是障碍消息,则取队列中第一个异步消息作为待发送的消息。如果取到的消息触发时间大于当前时间,则不返回该消息,进入下次循环先阻塞到消息触发时间后再返回,如果大于等于当前时间则直接返回,如果没有取到消息则一直阻塞队列。

IdleHandler 有什么用?

IdleHandler 是 Handler 提供的一种在消息队列空闲时,执行任务的机制,即空闲任务。当 MessageQueue 当前没有立即需要处理的消息时,会执行 IdleHandler。

MessageQueue 获取一次消息需要进行几次 for 循环?

最多三次,当取到的消息时间小于等于当前时间,只需要一次 for 循环。当取到的消息时间大于当前时间,如果没有 IdleHandler,需要两次 for 循环;如果有 IdleHandler,需要三次 for 循环。

当 mIdleHanders 一直不为空时,为什么不会进入死循环?

只有在 pendingIdleHandlerCount 为 -1 时,才会尝试执行 mIdleHander ,pendingIdlehanderCount 在 next() 中初始时为 -1,执行一遍后被置为 0,所以不会重复执行。

Handler内存泄漏

class HandlerActivity extends AppCompatActivity {
    private final Handler mHandler = MyHandler();
 
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler.sendEmptyMessageDelayed(1, 1000);
    }
 
    class MyHandler extends Handler {}
}

上面Handler的实现方式,当我们HandlerActivity在1000毫秒内退出就会产生内存泄漏。这是因为Handler被Message持有,保存在其target当中,而Message又被保存在MessageQueue中,所以Handler不会被GC,而Handler是一个内部类,持有HandlerActivity的引用,所以HandlerActivity也不会被回收,所以导致了内存泄漏。

主线程的Looper什么时候退出循环?

APP退出的时候。

//ActivityThread.java   H
public void handleMessage(Message msg) {
    switch (msg.what) {
        case EXIT_APPLICATION:
            if (mInitialApplication != null) {
                mInitialApplication.onTerminate();
            }
            Looper.myLooper().quit();
            break;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值