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技术可以参考文献《Android核心技术手册》点击前往查看。里面技术板块30多个技术点上千个。