追根溯源—— 探究Handler的实现原理

一、初识Handler

要分析一个框架的源码,首先需要了解它的使用。我们就从最基础的知识开始吧!

场景1:将数据从子线程发送到主线程。

我们知道Android系统中更新UI只能在主线程中进行。而通常网络请求实在子线程中进行的。在获取到服务器数据后我们需要切换到主线程来更新UI。通常在这种情况下我们会考虑使用Handler来进行线程切换。我们来模拟一个网络请求,并通过Handler将数据发送出去:


	private Handler mHandler = new Handler(){
	        @Override
	        public void dispatchMessage(@NonNull Message msg) {
	            super.dispatchMessage(msg);
	            updateUI(msg);
	        }
	    };
	// 或者可以通过传入Handler.Callback的参数
	Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
			updateUI(msg);
            return false;
        }
    });
	    
	// 模拟网络请求
	 private void fetchData() {
	        new Thread(){
	            @Override
	            public void run() {
	                super.run();
	                Message message = mHandler.obtainMessage();
	                message.obj="data from server";
	                mHandler.sendMessage(message);
	            }
	        }.start();
	
	    }

我们通过子线程模拟网络请求,并将获取的数据通过Handler的sendMessage方法将数据发送了出去。而在Handler的dispatchMessage方法中便可收到刚刚发送的message。并且此时dispatchMessage处于主线程,因此可以直接在dispatchMessage方法中更新UI了。

场景2:延迟发送Runnable

场景1是Handler很典型的一个应用,除此之外,我们还经常会通过Handler延迟发送一个Runnable,在delayMillis时间之后会执行Runnable的run方法。代码示例如下:

	// 假设是运行在Activity中的代码
	private Handler mHandler = new Handler();
	private Runnable mRunnable = new Runnable() {
	        @Override
	        public void run() {
	            handleMessage();
	        }
	    };
	 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
	 	new Thread(){
            @Override
            public void run() {
                super.run();
                mHandler.postDelayed(mRunnable,1000);
            }
        }.start();
	}

场景3:在子线程中初始化Handler

 Handler mHandler;
 new Thread(){
            @Override
            public void run() {
                super.run();
                mHandler=new Handler();
            }
        }.start();

此时,运行程序我们会发现程序Crash掉了,并且抛出了以下错误日志:

java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()

日志信息提示我们在子线程中创建Handler需要调用Looper.prepare()方法。

(注:以上场景均未考虑内存泄露问题)

那么针对以上三个应用场景,有些同学可能表示会有问题:

问题1: 在场景1中Handler在子线程中发送出来的消息为什么到了dispatchMessage方法就变成了主线程呢?

问题2: 在场景2的代码中我故意在子线程中调用了Handler的postDelayed方法,那么此时通过postDelayed方法发出来的Runnable究竟是运行在主线程还是子线程呢?

问题3: 为什么在主线程中直接创建Handler可以正常运行而在子线程中创建Handler需要调用Looper.prepare()方法呢?

如果想要解开以上几个疑惑,就需要我们对Handler的源码有深入的了解。那么接下来我们就进入Handler的源码分析环节吧!

二、Handler源码溯源历程

1.从实例化一个Handler说起

在使用Handler的第一步就需要我们先实例化一个Handler。那么,我们就从new Handler()着手,看看在初始化Handler的时候做了哪些操作:


	final Looper mLooper;
    final MessageQueue mQueue;
	final Callback mCallback;
    final boolean mAsynchronous;
    public Handler() {
        this(null, false);
    }

	public Handler(@Nullable Callback callback, boolean async) {
	        if (FIND_POTENTIAL_LEAKS) {
	            final Class<? extends Handler> klass = getClass();
	            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
	                    (klass.getModifiers() & Modifier.STATIC) == 0) {
	                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
	                    klass.getCanonicalName());
	            }
	        }
	
	        mLooper = Looper.myLooper();
	        if (mLooper == null) {
	            throw new RuntimeException(
	                "Can't create handler inside thread " + Thread.currentThread()
	                        + " that has not called Looper.prepare()");
	        }
	        mQueue = mLooper.mQueue;
	        mCallback = callback;
	        mAsynchronous = async;
	    }

可以看到,调用Handler的无参构造方法实例化Handler最终调用了Handler(@Nullable Callback callback, boolean async)的构造方法,而在这个构造方法中的逻辑似乎也很简单,通过调用Looper.myLooper()方法得到了一个Looper对象,如果此时mLooper为null,则会抛出一个RuntimeException。仔细看看发现这个异常信息不就是我们在第一章场景三中在子线程中初始化Handler的时候抛出来的异常吗?那么,什么时候通过Looper.myPrepare()方法得到的Handler会是空呢?要解决这个问题怕是要到Looper中一探究竟了。

首先来看下Looper.myPrepare()方法的源码:


    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

这是Looper中的一个静态方法,这个方法简单到只有一行代码,即调用了ThreadLocal的get()方法来得到一个Looper。ThreadLocal是Java中的一个类,它是一个泛型类,有set(T)和T get()两个方法。ThreadLocal的神奇之处在于它可以在指定线程中存储数据,什么意思呢?即你在某个线程中存储的数据只能在该线程中获取的到。关于ThreadLocal我们只需要了解这些就够了,其实现原理不做深究,有兴趣的可以自从查阅源码。

既然了解了ThreadLocal的作用,那么在Looper内部一定事先调用了ThreadLocal的set方法。通过全局搜索sThreadLocal,发现了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的prepare方法中实例化了Looper,并将其放入到了ThreadLocal中。而如果没有调用Looper.prepare()方法,那么获取到的Looper一定是null。到这里似乎解释了第一章的场景三在子线程中实例化Handler需要事先调用Looper.prepare()方法。但是,到这里我们似乎还是没有解释清楚疑惑三,为什么在主线程中直接实例化Handler却没有抛出异常呢?其实,当程序启动的时候会同时启动主线程,我们知道Java程序的入口是main方法。而在Android程序启动的入口同样是main方法。我们在ActivityThread中找到main方法,源码如下:

	
	# ActivityThread
	public static void main(String[] args) {
	        
			...
	
	        Looper.prepareMainLooper();
			...

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

	 # Looper
	 public static void prepareMainLooper() {
	        prepare(false);
	        synchronized (Looper.class) {
	            if (sMainLooper != null) {
	                throw new IllegalStateException("The main Looper has already been prepared.");
	            }
	            sMainLooper = myLooper();
	        }
	    }


在main方法中调用了Looper.prepareMauinLooper()方法,而Looper.prepareMauinLooper()中又调用了prepare方法。因此,我们可以在主线程中直接实例化Handler而不用担心抛出异常。

2.Handler的postXXX()或sendXXX

实例化Handler后,我们通常会通过Handler提供的方法来发送一个消息。在第一章节中列举了Handler的sendMessage()和postDelay()方法,其实除了这两个方法之外,Handler还提供了一系列的sendXXX()和postXXX()的方法。但追踪源码发现这些方法最终都会调用到sendMessageAtTime方法。

我们就以postDelayed方法为例来分析。首先,来看postDelayed的源码:

public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

在postDelayed中调用了sendMessageDelayed方法,这个方法的第一个参数接收一个Message,这里通过getPostMessage®方法得到了Message对象,getPostMessage®源码如下:

	private static Message getPostMessage(Runnable r) {
	        Message m = Message.obtain();
	        m.callback = r;
	        return m;
	    }

可以看到,在getPostMessage方法中将Runnable赋值给了Message.callback,此处我们先立一个Flag。

接下来追溯到sendMessageDelayed方法中:

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

在这个方法中我们终于看到了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);
    }

这个方法中的代码逻辑很简单,仅仅调用了enqueueMessage方法,源码如下:

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

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

在这个方法中我们需要注意的一点-- msg.target = this,把当前Handler的对象赋值给了Message.target。紧接着调用了MessageQueue的enqueueMessage方法。MessageQueue这个类我们在上边已经碰到过了。即在实例化Handler的时候将mLooper.mQueue赋值给了Handler的mQueue.那么现在是时候来认识一下MessageQueue了。

3.MessageQueue

上一小节中我们在Handler的构造方法中看到了将mLooper.mQueue赋值给了Handler中的mQueue。而MessageQueue从名字上似乎也可以看出来它是一个存储消息的队列。并且在Handler的enqueueMessage方法中通过MessageQueue的enqueueMessage将消息添加到了MessageQueue中。接下来,我们看下enqueueMessage的源码:

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        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 {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                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;
            }

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

emmmmmm…阅读源码发现MessageQueue是一个用单项链表实现的队列,为什么是用链表?其实细想一下也不奇怪,因为我们可能频繁的将Message进行插入和删除。而使用链表性能上很明显要优于顺序表。这一段代码就是一个将Message插入到链表的一个过程,其实也没什么可以解释的。

既然有插入消息,那必然也会有取出消息。我们进入MessageQueue的源码,发现在MessageQueue中发现还有另外一个叫next的方法:

Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            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) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        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;
                }

                // 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();
                }
                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,很显然,这个方法就是从MessageQueue中取出消息的。我们注意到第12行代码,这里是一个for(;;)死循环。如果在MessageQueue中有Message的时候,next会将这个Message移除并返回。而当MessageQueue为空时,next方法将会被阻塞。

4.回归Looper

我们了解了MessageQueue的插入和删除,但直到此时还没有看到在什么地方调用了MessageQueue的next方法将消息取出的。此时,我们将目光再次回归到Looper中。如果你刚才留意了ActivityThread中的main方法的话,应该注意到了main方法中的Looper.loop()方法,没错,一切将从这里开始,只有调用loop方法后消息循环系统才会真正运作起来,我们们来看loop的源码:

 /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the 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;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        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;
            }

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

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

这里的loop方法同样也是一个死循环。只有当MessageQueue.next()返回null时才会跳出循环。上一小节中我们分析了Message.next方法是一个阻塞操作当MessageQueue中没有消息时next方法会一直被阻塞,因此也致使loop方法被阻塞。而当MessageQueue中添加了一条消息后,loop方法则会立即通过MessageQueue.next方法将消息取出并处理。可以看到通过调用msg.target.dispatchMessage(msg)处理消息。而msg.target是什么呢?我们回过头来看本章第二小节,在Handler的enqueueMessage中将Handler赋值给了msg.target,我们从Message的源码中也可以得到印证:

public final class Message implements Parcelable {
	...
	
  /*package*/ Handler target;
	
	...
}

也就是此时的target其实就是Handler本身,此时我们找到Handler的dispatchMessage方法,源码如下:


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

看到这里想必大家应该就都明白了,上述代码首先检查了Message的callback是否为null,不为null就通过handleCallback来处理消息。Message的callback是一个Runnable对象,实际上就是Handler的post方法传递的Runnable参数,此时对应的是我们在第一章中的场景二。handleCallback方法代码如下:

private static void handleCallback(Message message) {
        message.callback.run();
    }

emmmm…原来我们post出来的Runnable的run方法就是在这里被调用的呀!

接着检查mCallback是否为null,不为null就调用mCallback的handleMessage方法处理消息,这里对应的是我们在实例化Handler时传入的Callback参数。如果mCallback也为null的话则调用了Handler中的handleMessage(msg)方法。这不正对应了我们第一章的场景一嘛!因此现在再来看下面的代码会不会豁然开朗呢?


//		实例化Handler对象 传入Callback参数并重写handleMessage(Message msg)
 static Handler mHandler1 = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {

            return false;
        }
    });

//	实例化Handler对象 并重写Handler中的handleMessage(Message msg)
handleMessage(Message msg)
 static Handler mHandler = new Handler(){
		  @Override
        public boolean handleMessage(Message msg) {

            return false;
        }
};

到这里我们总算把Handler的消息机制给串联了起来。

三、总结

Handler、Looper和MessageQueue三者共支撑了Android中的消息机制。在使用的时候我们通常只需要和Handler打交道,通过Handler将消息发送出来。而在Handler内部的MessageQueue通过equeueMessage方法将Handler发送的消息以队列的形式进行存储。而Looper则作为一个消息泵,通过loop方法不断的调用MessageQueue的next方法将消息取出并进行处理,依次完成了整个的消息循环。同时,我们还应该特别强调ThreadLocal,正是有了它的存在,才使得我们能够轻松的进行切换线程。

整个过程当如下图所示:

这里写图片描述

看到这里,在第一章中提到的几点疑惑相信你心中应该也已经有了答案。我们不妨再来总结一下:

问题1: 在场景1中Handler在子线程中发送出来的消息为什么到了dispatchMessage方法就变成了主线程呢?
虽然post消息是在子线程,但因为Handler是在主线程中被实例化的,由于在ActivityThread的main方法中调用了Looper.prepare(),因此,通过ThreadLocal将Looper缓存到了主线程中,而MessageQueue是Looper的成员变量,所以其也应是在主线程中。同时,Looper.loop方法将Message取出并通过Message.target.handlerMessage方法最终调用了dispatchMessage方法,所以dispatchMessage方法当然也是在主线程了。

问题2: 在场景2的代码中我故意在子线程中调用了Handler的postDelayed方法,那么此时通过postDelayed方法发出来的Runnable究竟是运行在主线程还是子线程呢?

这个问题其实和问题1本质上是同一个问题,就无需多言了!

问题3在文中已经给出了答案,这里也不再赘述。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值