Handler到底是如何完成线程切换的?

Handler到底是如何完成线程切换的?这个问题要从Handler最初的用法和原理讲起。

首先我们列出正常情况下一个Handler使用的步骤然后讲解分析如何实现,这里不对一些基础的概念做解释,具体的请查阅源码。

Handler的使用步骤

1.调用Looper.prepare();

2.创建Handler对象;

3.调用Looper.Loop()方法。

4.线程中发送消息。

这样我们就创建好了一个Handler可以采用sendMessage等方法发送消息。

那么这几步都干了什么?

第一步:调用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));
}

创建了一个Looper对象保存在了一个ThreadLocal对象中,至于ThreadLocal是干什么的,查询有关ThreadLocal如何存储线程中数据的资料

上面代码中创建了一个Loper对象,new Looper的时候我们可以看到:

private Looper(boolean quitAllowed) {
      mQueue = new MessageQueue(quitAllowed);
      mThread = Thread.currentThread();
 }
// 创建了一个MessageQueue对象,这个就是一个消息队列用来保存我们发送的Message,其实这个MessageQUueue是一个单链表。

后边我们会知道这里创建了一个MessageQueue对象,从字面上来看就是一个消息队列,其实他是一个链表,链表的数据结构就是便于插入和删除。

第二步创建Handler

public Handler(Callback callback, boolean async) {
        ......
        mLooper = Looper.myLooper();
        ......
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

从构造方法中我们知道了Hanlder中获取了一个Looper对象,这个Looper中保存了一个mQueue.这个Lopper对象就是我们前面保存在ThreadLocal中的那个。

第三步调用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;
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        for (;;) {// 无限循环
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ......
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {// 处理消息
                msg.target.dispatchMessage(msg);
            ......
    }

我们清楚的看到:loop()方法中获取了Looper对象,取出了消息队列mQueue,然后开始循环。同时调用了MessageQueue的next()方法取出一个消息最后调用了这个消息的dispathchMessage();看完源码我们知道这个msg.tagget.dispatchMessage()就是Handler中的消息处理的方法。

 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

这个方法的代码中我们看到,首先判断了callback是不是null.这是因为Handler使用有两种方法,一种可以通过创建CallBack的方式。最后调用了Hanlder的handleMessage(msg);这个方法就是我们经常自己实现的消息处理方法。所以我们就到达了目标。

第四步:发送消息

调用sendMessage()等方法发送消息,追踪发送消息的方法,最后调用了以下的方法:

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构造方法保存的mQueue,然后调用了enqueueMessage()方法。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

最后看queue.enqueueMessage(msg, uptimeMillis);在queue中处理了消息。

boolean enqueueMessage(Message msg, long when) {
       synchronized (this) {// 同步的操作,同一时间只能一个线程操作
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }
            ......
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {// 判断消息要执行的时间顺序
                // New head, wake up the event queue if blocked.
                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;
            }
        }
        return true;
    }

也就是通过消息要执行的时间,最后把消息插入链表中。

这样我们就完成了消息的发送!

此刻:主线程的loop()方法一直在循环处理着消息,我们发送的消息就会到loop()方法中去,最后交给Handler处理。

那么到底是怎么样就完成了线程的切换呢?

其实就是这个消息发送的过程,我们在不同的线程发送消息,线程之间的资源是共享的。也就是任何变量在任何线程都可以修改,只要做并发操作就好了。上述代码中插入队列就是加锁的synchronized,Handler中我们使用的是同一个MessageQueue对象,同一时间只能一个线程对消息进行入队操作。消息存储到队列中后,主线程的Looper还在一直循环loop()处理。这样主线程就能拿到子线程存储的Message对象,在我们没有看见的时候完成了线程的切换。

所以总结来讲就是:

1.创建了一个Looper对象保存在ThreadLocal中。这个Looper同时持有一个MessageQueue对象。

2.创建Handler获取到Looper对象和MessageQueue对象。在调用sendMessage方法的时候在不同的线程(子线程)中把消息插入MessageQueue队列。

3.在主线程中(UI线程),调用Looper的loop()方法无限循环查询MessageQueue队列是否有消息保存了。有消息就取出来调用dispatchMessage()方法处理。这个方法最终调用了我们自己重写了消息处理方法handleMessage(msg);这样就完成消息从子线程到主线程的无声切换。

最后补充:我们经常使用Handler没有创建Looper调用Looper.pepare()和Looper.loop()是因为在ActivityThread中已经创建了主线程的Looper对象,保存在了ThreadLocal中,我们在创建Hander的时候就会从ThreadLocal中取出来这个Looper。

从主线程类ActivityThread的入口方法main()方法中我们可以清楚的看到系统帮我们如何实现的:

  public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();// 调用了prepareMainLooper(),创建Looper和MessageQueue

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();// 获取Handler
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();// 开始调用loop()方法

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值