Handler相关知识

1、概念

Handler可以通过与线程相关的MessageQueue发送和处理Message和Runnable对象。通过这种机制,Android可以在相同进程中不同线程间通信。

2、Handler类

在源码中,Handler类中持有对Looper与MessageQueue的引用,如下:
Handler对Looper及MessageQueue的引用
这两个变量在构造方法执行时会赋值,Handler提供了多个构造方法,如下
Handler构造函数
通过传入不同的Looper及Callback可以构造不同的Handler对象。比较常用的是不传任何参数的第一个构造方法。这些方法最终都会调用如上图中的第四个构造方法,其中主要逻辑如下:

public Handler(Callback callback, boolean async) {
        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;
    }

构造方法中,首先会获取当前线程的Looper对象,如果为空的话则抛出异常。下面是Looper中myLooper的方法:
Looper.myLooper()
从代码中可以看出,Looper获取的是与当前线程关联的Looper对象。而这个对象的设置是在prepare()方法中, 如下
Looper.prepare()
所以在一个新的线程中使用Handler时,如果不调用Looper.prepare()方法会抛出异常。而在主线程中直接new Handler()没有报错,是因为在主线程启动时调用了相关的代码,如下

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

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        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
        Looper.prepareMainLooper();

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

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

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        // 开启循环,这里面是无限循环,基本不可能走到后的抛异常。
        // 因为一个线程执行完成了就会退出,所以需要阻塞住线程。
        Looper.loop();

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

有了Handler对象之后,我们就可以使用Handler来发送消息了。Handler类提供的多种发送消息的方法,如下:
sendMessage
这些方法最终都会调用到sendMessageAtTime,其代码如下:

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

从代码中可以看到主要的逻辑是调用了enqueueMessage方法,即将消息加入队列。enqueueMessage的方法如下:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
	// 指定msg的target对象,即为当前Handler
    msg.target = this;
    // 设置消息是否同步,一般的消息都是同步的。源码中有一些用到了异步的消息。
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    // 此处的queue是MessageQueue对象,通过Looper对象获取。 
    return queue.enqueueMessage(msg, uptimeMillis);
}

从代码中可以看出,Handler的sendMessage只是将message加入到了MessageQueue的队列中。 至此Handler完成了消息的发送。

3、MessageQueue

上节中Handler发送消息后实际上是调用了MessageQueue的enqueueMessage方法,代码如下:

boolean enqueueMessage(Message msg, long when) {
	// message的target类型为Handler。Handler中调用enqueueMessage方法时已经指定了该message的target对象了。
    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;	// mMessages相当于链表的头节点。Message相当于链表中的节点,持有下一个节点的引用next。
        boolean needWake;
        // 如果当前节点为空 或 msg的触发时间为0 或 触发的时间早于头节点,则将该msg插入到头节点,并赋值为新的头节点
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;	// 将msg设置为新的头节点
            needWake = mBlocked;	// 根据block状态来确定是否需要唤醒
        } 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.
            // 如果消息是异步的 或 target为空的情况需要wake
            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;
                }
            }
            // 此时p是null,相当于将p插入到链表尾部。
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
        	// 调用native方法唤醒(因为looper对象在for循环中调用next方法取消息,而取消息的方法会阻塞线程)
            nativeWake(mPtr);
        }
    }
    return true;
}

该方法相当于将消息放到了消息链表的尾部,同时根据消息是否是同步还是异步来唤醒线程。
至此,已经将消息放到链表这样的数据结构了,接下来肯定是要取出这些消息,然后进行相应的操作的。 消息的取出是通过Looper调用了MessageQueue的next方法实现的,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();
        }

		// 根据nextPollTimeoutMillis的值不同,这里可能会发生阻塞
		// -1时会无限阻塞,直到被唤醒。0表示不阻塞,直接返回。其他正数,则会阻塞相应的时间,然后返回。
		// 所以刚进来没有消息时,会一直阻塞在这里。
		// 在handler发送消息后,MessageQueue的enqueueMessage方法中就会唤醒这里的线程,跳过这里,从而取出消息处理。
        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;
            // target为空的情况一般在源码中。
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                // 当链表头message的target为空的情况下,找到第一个异步消息。故这之间的同步消息会被忽略掉。 
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
            	// 如果消息的处理时间还未到,则设置nextPollTimeoutMillis
                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; 	// 将prevMsg与msg后面的节点链接起来。
                    } 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;
    }
}

通过该方法,就可以取出一个消息了。拿到消息后,Looper中就会做相应处理。

3.1、MessageQueue的native层

在MessageQueue的源码中使用如下的native代码:

private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);

在native层与之对应的类是NativeMessageQueue。通过对源码的查看,可以得知MessageQueue的阻塞与唤醒是通过epoll机制实现的。(这里还需要深入了解)

4、Looper

Looper类的loop会调用MessageQueue类的next方法取出消息,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;
        }

   		// ...... 省略
        final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
        final long dispatchEnd;
        try {
        	// 最主要的是这行代码,调用target的dispatchMessage方法。 
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        // ...... 省略

        msg.recycleUnchecked();	// 回收消息
    }
}

从代码中看,最重要的是msg调用了target对象的dispatchMessage方法,而这个target即是Handler类型,也即是在sendMessage时赋值的。由此可以知道,消息的发送和处理均是在Handler中处理的。Handler中dispatchMessage方法如下

 /**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
	// 如果消息中携带有callback参数,callback类型为Runnable
    if (msg.callback != null) {
        handleCallback(msg);	// 这个方法会直接调用message.callback.run()
    } else {
    	// 如果在构造Handler时传入了Callback参数,则用此Callback对象处理消息
        if (mCallback != null) {
        	// 如果callback的handleMessage方法返回false,还会继续用handler自身的方法。
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
		// 常见的构造handler时需要覆写的方法,handler中该方法为空实现。
        handleMessage(msg);
    }
}

到这里,即完成了消息的分发与处理。

5、Message

Message类相当于链表中的一个节点,因为其保存了对下一个节点的引用,即next变量。同时还定义了what,arg1,arg2、data,callback成员变量。获取Message的方法除了直接new之外,系统还提供了一个obtain方法从缓存池中获取,代码如下:

/**
 * Return a new Message instance from the global pool. Allows us to
 * avoid allocating new objects in many cases.
 */
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;	// 此处的sPool指向链表的头节点
            sPool = m.next;
            m.next = null;	// 断开与头节点的链接
            m.flags = 0; // clear in-use flag
            sPoolSize--;	// 缓存池大小减1
            return m;
        }
    }
	// 如果没有缓存则new一个Message
    return new Message();
}

而缓存池的释放则是在message的recycleUnchecked方法中,如下

/**
 * Recycles a Message that may be in-use.
 * Used internally by the MessageQueue and Looper when disposing of queued Messages.
 */
void recycleUnchecked() {
    // Mark the message as in use while it remains in the recycled object pool.
    // Clear out all other details.
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = -1;
    when = 0;
    target = null;
    callback = null;
    data = null;

    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
        	// 采用头插法,将回收到的Message放在链表的头部
        	// 当前节点的next链接到原链表头部。
            next = sPool;	
            // 将当前节点作为新头节点
            sPool = this;
            // 缓存池大小加1	
            sPoolSize++;
        }
    }
}
6、IdleHandler

在MessageQueue的next方法中,如果消息为空的情况下,会调用IdleHandler对象的queueIdle方法,而Idlehandler对象可以通过MessageQueue暴露的接口添加多个。使用方法如下:

Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
    @Override
    public boolean queueIdle() {
        Log.i("test", "idleHandler queueIdle " + System.currentTimeMillis());
       // 返回true时,会保持该IdleHandler一直活跃。false则会移除该IdleHandler。
        return true;
    }
});

该方法的注释说明了调用的场景

/**
* Called when the message queue has run out of messages and will now
* wait for more. Return true to keep your idle handler active, false
* to have it removed. This may be called if there are still messages
* pending in the queue, but they are all scheduled to be dispatched
* after the current time.
*/

我们可以通过此方法将一些操作在没有消息时处理,从而避免抢占资源优化交互体验。

7、总结

1、构造Handler时,会通过ThreadLocal获取Looper对象及其MessageQueue对象。Handler发送消息时会调用MessageQueue的enqueueMessage方法将消息加入到链表中去。同时,Looper的loop循环会调用MessageQueue的next方法取出一个消息,调用该消息的dispatchMessage方法,从而将消息分发出去。
2、其中,MessageQueue的next方法中的nativePollOnce会阻塞住线程,而enqueueMessage中会调用nativeWake唤醒线程。
3、主线程中的Looper会阻塞住主线程。由于线程的特性,线程执行完成后就会退出,故ActivityThread类中的Loop.loop方法的无限循环会阻塞住主线程,从而无法走到最后一行的抛出异常。在主线程阻塞没有引起ANR的原因是发生阻塞时没有事件发生,而当有事件发生时,系统会唤醒线程从而避免了ANR。即有事件时唤醒线程处理,无事件时阻塞等待。
4、在一个handler对象上removeMessage时需要注意的是,只能该handler对象的messgae。当出现message不能删除的时候,要检查一下该message是不是关联到当前的handler对象。
5、进程阻塞时,由于操作系统没有分配执行片段给该进程,所以该进程一直在等待状态,不会占用相关资源。

8、参考

以下三篇文章写的非常好。
1、MessageQueue
2、Android中线程间通信机制Handler疑问解答
3、进程阻塞为什么不会占用cpu资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值