Android_Handler源码阅读

Handler

准备面试,花时间复习一下Handler。
我们知道,更新UI时需要在UI线程进行,但是网络访问、访问数据库等耗时操作一般会开辟一个新的线程或者交给线程池处理。那么在获得结果的时候,可以通过Handler将结果发送给UI线程,去更新UI。

Handler源码解析

我们先看一下构造,一般用的就是前两个

public Handler(@NonNull Looper looper) {
    this(looper, null, false);
}

public Handler(@NonNull Looper looper, @Nullable Callback callback) {
    this(looper, callback, false);
}

public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

可以看到,构造就是做了一些变量赋值的操作。这里面需要关注的时Looper。使用时,假如如果我们需要切换到主线程,会传入Looper.getMainLooper()。至于Looper做了什么事情,我们后面再看。这里只需要知道,Handler通过Looper拿到了当前线程的MessageQueue

那么我们发送消息的时候,会通过sendMessage(Message msg)或者post(Runnable r)发送。这两个方法都会走到sendMessageAtTime()

public boolean sendMessageAtTime(@NonNull 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);
}

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this;// 1.指定目标Handler为当前handler
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

	// 如果是个异步Handler,那么把消息设为异步消息
    if (mAsynchronous) { 
        msg.setAsynchronous(true);
    }
    // 2. 把msg插入到消息队列中
    return queue.enqueueMessage(msg, uptimeMillis);
}

上面可以看到,直接调用MessageQueue#enqueueMessage()把消息插入到消息队列中。

boolean enqueueMessage(Message msg, long when) {
        ...
        synchronized (this) {
        	// 消息不可重复发送
            if (msg.isInUse()) { 
                throw new IllegalStateException(msg + " This message is already in use.");
            }
			...
            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();
               	// 遍历消息队列,根据时间戳when按顺序插入对消息队列中,这个消息队列实际上是一个链表
                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;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

回顾一下发消息的流程:

  1. 首先通过sendMessage()/post()方法发送之后,都会走到sendMessageAtTime()之中,这个方法中会调用MessageQueue#enqueue()方法将消息插入到线程的消息队列之中;
  2. MessageQueue#enqueue()会遍历消息队列,emm实际是一个链表,按照时间戳when将消息按顺序插入到消息队列中。

既然有插入消息的方法,那就肯定有取出消息的方法。插入消息是我们调用,sendMessage()发的,那谁在取消息呢?就是Looper啊,通过loop()方法一直再拿!!

public final class Looper {
    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
    private static Looper sMainLooper;  // guarded by Looper.class
    final MessageQueue mQueue;
    final Thread mThread;

	...

    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));
    }

    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

public static void loop() {
        final Looper me = myLooper();
		...
        for (;;) {
            if (!loopOnce(me, ident, thresholdOverride)) {
                return;
            }
        }
    }

	 private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {
        //取消息
        Message msg = me.mQueue.next(); 
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return false;
        }

        // This must be in a local variable, in case a UI event sets the logger
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " "
                    + msg.callback + ": " + msg.what);
        }
		
		...
		
        try {
        	// 发送给目标Handler
            msg.target.dispatchMessage(msg);
            ...
        } catch (Exception exception) {...} finally {...}
		...
        msg.recycleUnchecked();
        return true;
    }
}

// Handler#dispatchMessage()
public void dispatchMessage(@NonNull Message msg) {
	//1. 如果msg设置的callback
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
    	// 2. 处理Handler的callback
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 3. 调用重写的handlerMessage()方法
        handleMessage(msg);
    }
}

Looper是怎么获取的呢?我们可以看到Looper类里面维护了一个static类型的ThreadLocal,那么只要线程一开始执行了Looper.prepare()(当然别忘了调loop()),不同的线程只需要调用get()就可以返回线程对应的Looper了。这里具体怎么做的另一篇文章讲叭。。。

再点击去看一下MessageQueue#next()方法

Message next() {
     ...

     int pendingIdleHandlerCount = -1; // -1 only during first iteration

     int nextPollTimeoutMillis = 0;
     for (;;) {
         if (nextPollTimeoutMillis != 0) {
             Binder.flushPendingCommands();
         }
		 // 阻塞nextPollTimeoutMillis
         nativePollOnce(ptr, nextPollTimeoutMillis);

         synchronized (this) {
             // Try to retrieve the next message.  Return if found.
             final long now = SystemClock.uptimeMillis();
             Message prevMsg = null;
             Message msg = mMessages;
             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 {
                     // 取出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;
             }
   			 ...
			
             // If first time idle, then get the number of idlers to run.
             // Idle handles only run if the queue is empty or if the first message
             // in the queue (possibly a barrier) is due to be handled in the future.
             if (pendingIdleHandlerCount < 0
                     && (mMessages == null || now < mMessages.when)) {
                 pendingIdleHandlerCount = mIdleHandlers.size();
             }
             // 没有要执行的IdleHandler,那么就continue,就会到下一轮循环然后阻塞
             if (pendingIdleHandlerCount <= 0) {
                 // No idle handlers to run.  Loop and wait some more.
                 mBlocked = true;
                 continue;
             }

             if (mPendingIdleHandlers == null) {
                 mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
             }
             mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
         }

         // Run the idle handlers.
         // We only ever reach this code block during the first iteration.
         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);
             }

             if (!keep) {
                 synchronized (this) {
                     mIdleHandlers.remove(idler);
                 }
             }
         }

         // Reset the idle handler count to 0 so we do not run them again.
         pendingIdleHandlerCount = 0;

         // While calling an idle handler, a new message could have been delivered
         // so go back and look again for a pending message without waiting.
         nextPollTimeoutMillis = 0;
     }
 }

可以看到Message#next()方法就是链表中取出一个Message,但是时间没有到的话,会阻塞相应的时间。

3. Handler工作原理总结

经过上面的源码分析,我们可以大致了解Handler的工作原理:

  1. 首先通过sendMessage()/post()方法发送之后,都会走到sendMessageAtTime()之中,这个方法中会调用MessageQueue#enqueue()方法将消息插入到线程的消息队列之中;
  2. MessageQueue#enqueue()会遍历消息队列,emm实际是一个链表,按照时间戳when将消息按顺序插入到消息队列中。
  3. 那么既然有插入就一定要有取出,取出是由Looper完成的。Looper.loop()是一个死循环,会不断调用MessageQueue#next()方法取出消息。然后dispatchMessage()分发到目标Handler中。如果msg设置了callback,那就会执行msg的callback;如果没有,就执行Handlercallback,在执行我们重写的handleMessage()方法。当然,这个next()是一个阻塞方法,如果没有消息就会阻塞。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值