Handler的消息机制与消息延迟代码实现

565 阅读4分钟

Handler的作用

Handler消息机制在Android中的应用非常广泛,很多组件的底层实现都是靠Handler来完成的,所以掌握Handler消息机制的原理还是非常重要的。Handler的主要功能有两点:

1.它可以在不同的线程之间传递消息

我们都知道Andorid中规定只有主线程里可以进行UI操作,在其他线程中绝不可以进行与UI相关的操作,否则会报错。还有就是在主线程中也不可以进行耗时操作,否则会阻塞主线程,报ANR。基于以上这两点,我们就可以使用Handler在子线程中处理耗时操作,然后把返回的结果传给主线程,再进行UI方面的更新处理等。

2.它可以延时传递消息。

如果我们想要处理一个延时操作,一般可以通过Thread.sleep(times)使线程休眠几秒,然后再处理数据。但是再Android中是绝不能允许在主线程红进行休眠操作阻塞线程的,所以我们可以通过Handler来发送延迟消息的方式实现延时操作。

消息处理机制

Android的消息处理机制表面上只是使用了Handler,但其实还用到了其他的东西,比如说ThreadLocal,Looper,MessageQuery等,这些组合在一起使用,才构成了整个Android消息机制。

我们先回顾一下Handler的使用:我们以 “在子线程处理完耗时操作,然后通过Handler发送消息到主线程更新UI”为例。首先要先在主线程创建一个Handler对象,重写它的handlerMessage()方法,处理更新UI的操作。然后我们在子线程进行处理耗时操作,当执行完成后,我们创建一个Message对象,将需要传递的数据存入Message对象中,再调用Handler对象的sendMessage()方法将该Message对象发送出去。之后我们就能在Handler的handlerMessage()方法中接收到该Message对象了,然后取出其中的数据,进行更新UI的操作即可,这样整个过程就算完成了。

上面说的例子是从子线程发送消息回主线程,然后再主线程中处理消息。如果是通过主线程发送消息到子线程,然后在子线程处理消息,那又应该如何做呢?我们尝试直接在子线程中创建Handler对象,重写handlerMessage()方法,然后在主线程中发送消息,但程序运行后会报如下错误:

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

意思是“不能够在一个没有执行过Looper.prepare()方法的线程中去创建Handler对象”,到这里我们终于能够看到Looper的影子了。那么这里就有一个问题,为什么在主线程中没有要求我们先调用Looper.prepare()方法呢,或者说与普通线程相比,主线程有什么特别的吗?

其实这是因为在主线程中,系统已经调用过该方法了,所以不需要我们再去手动调用。代码如下,主线程在刚开始创建的时候它的Looper就已经调用了prepareMianLooper()方法,所以我们能直接创建Handler。

public final class ActivityThread { 
   public static void main(String[] args) {
​
        ...
​
        Looper.prepareMainLooper();
​
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
​
        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 的延迟发送是依靠

nativePollOnce(ptr, nextPollTimeoutMillis);

这个方法中的nextPollTimeoutMills 表明了阻塞等待时间。

nextPollTimeoutMillis = (int)Math.min(msg.when - now, Interge.MAX_VALUE);

原理:

当sendMessage 时,enququeMessage()@MessageQueue 会将消息按照按照实际执行时间顺序(即msg.when)插入到队列,然后执行nativeWake()唤醒,随后执行到next()@MessageQueue 中的nativePollOnce()处,这里是一个 for 死循环,唤醒后计算出根据now 和msg.when,如果now < msg.when 则计算出nextPollTimeoutMillis,下一个循环赋值给nativePollOnce(ptr,nextPollTimeoutMillis),告诉阻塞时间。

其实 Java 层的 Handler 只是利用到了 native 层的 NativeMessageQueue和 native 层的 Looper 来完成阻塞和唤醒工作。消息队列的插入和消息的读取都是在 Java 层完成的 。当MessageQueue 在nativePollOnce()被nextPollTimeoutMillis 阻塞时,当有另外一个消息入队进行唤醒时,nativePollOnce()的阻塞状态立即被唤醒,然后进行处理消息,当发现now < msg.when 时,根据新的阻塞时间进行等待。因为消息在入队时根据msg.when 进行排列了。

代码实现

Handler mHandler;
​
mHandler=new Handler(){
public void dispatchMessage(android.os.Message msg) {
switch (msg.what) {
case 1:
Intent mIntent=new Intent();
mIntent.setClass(SetPassActivity.this, MainActivity.class);
startActivity(mIntent);
break;
​
default:
break;
}
};
};
mHandler.sendEmptyMessageDelayed(1, 2000);

以上就是Handler的消息机制介绍以及他的消息延迟操作代码实现;想要深入了解Handler的知识点,或者更多Android技术可以参考文献传送直达↓↓↓ :link.juejin.cn/?target=htt…点击前往查看。里面技术板块30多个技术点上千个。

文末

Handler 机制源码图解