Android开发人员对Handler,应该都很熟悉了,我们经常使用它的一个场景是当一些比较昂贵的耗时任务完成后,使用Handler通知到UI线程刷新UI,下面是代码1: Thread taskThread = new Thread(new Runnable() {2:3: @Override4: public void run() {5: //do some expensive tasks.6: downloadFile();7: mHandler.sendMessage(new Message());8: }9: });10: taskThread.start();非常的简单,我们会在mHandler的 handleMessage 回调方法里面收到任务完成后传递的message对象。今天我们深入去学习下Handler的实现源码。* <p>There are two main uses for a Handler: (1) to schedule messages and
* runnables to be executed as some point in the future; and (2) to enqueue
* an action to be performed on a different thread than your own.这个是Handler源码里面的注释,说明了Handler的2个主要用途:1.安排消息和任务在未来的某个时刻执行。2.将一个需要执行的动作放入另外一个线程的任务队列中。我们刚才演示的代码,其实就是在非UI线程中,将一个刷新UI的动作通知到UI线程。
下面我们看看Handler的默认构造器:
1: public Handler() {2: if (FIND_POTENTIAL_LEAKS) {3: final Class<? extends Handler> klass = getClass();4: if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&5: (klass.getModifiers() & Modifier.STATIC) == 0) {6: Log.w(TAG, "The following Handler class should be static or leaks might occur: " +7: klass.getCanonicalName());8: }9: }10:11: mLooper = Looper.myLooper();12: if (mLooper == null) {13: throw new RuntimeException(14: "Can‘t create handler inside thread that has not called Looper.prepare()");15: }16: mQueue = mLooper.mQueue;17: mCallback = null;18: }这里我们首先要弄清楚2个对象,Looper mLooper和 MeesageQueue mQueue,Looper类,可以为每一个线程起一个消息循环,然后每一个Looper内部会绑定一个MessageQueue对象,当有消息被放入消息队列时,消息循环会负责对消息进行分发。为线程new一个Looper,调用Looper.parpare();1: public static void prepare() {2: if (sThreadLocal.get() != null) {3: throw new RuntimeException("Only one Looper may be created per thread");4: }5: sThreadLocal.set(new Looper());6: }
1: static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
mThreadLocal保证每一个线程只会有一个Looper,重复创建会抛出异常。
将线程绑定的Looper运行起来 ,调用Looper.loop();下面looper方法的一个片段
1: public static void loop() {2: Looper me = myLooper();3: if (me == null) {4: throw new RuntimeException("No Looper; Looper.prepare() wasn‘t called on this thread.");5: }6: MessageQueue queue = me.mQueue;7:8: // Make sure the identity of this thread is that of the local process,9: // and keep track of what that identity token actually is.10: Binder.clearCallingIdentity();11: final long ident = Binder.clearCallingIdentity();12:13: while (true) {14: Message msg = queue.next(); // might block15: if (msg != null) {16: if (msg.target == null) {17: // No target is a magic identifier for the quit message.18: return;19: }
在循环中,从消息队列取消息时,如果队列是空的,循环会阻塞,等待消息的到来。到这里,我们清楚了,起一个Looper需要调用parpare和loop二个方法,但是我们在最开始的实例代码里面并没有做这样的调用啊,为什么也能正常的发送消息?这是因为我们的app启动时就会有一个主线程即UI线程,在主线程初始化时的时候已经调用了prepareMainLooper 和 loop方法。所以我们在非UI线程创建Handler会有下面的实例代码:
1: Thread backgroundThread = new Thread(new Runnable() {2:3: @Override4: public void run() {5: Looper.prepare();6: mBackgroundHandler = new Handler(){7: @Override8: public void handleMessage(Message msg) {9: super.handleMessage(msg);10: }11: };12: Looper.loop();13: }14: });
这样我们就可以利用mHandler和mBackgroundHandler,自由的将任务插入到它们各自所属线程的消息队列中。
现在我们再说说Message,Message类有二个比较重要的属性,Handler target和 Runnable callback,target表示这个消息要发给哪个handler去接收,如果某个消息没有target,Looper会认为这是结束消息循环的信号,于是Looper就会退出,所以一般我们用哪个Handler对象发送的消息,消息的target就是那个Handler对象,然后callback 就是我们上面说的 Handler二个用途的第一个用途,当从消息队列中取到消息后,会检查消息的target和callback,如果target不为空,callback也不为空,会执行callback的run方法。
Looper的退出,我们可以调用Looper.myLooper().quit();
1: public void quit() {2: Message msg = Message.obtain();3: // NOTE: By enqueueing directly into the message queue, the4: // message is left with a null target. This is how we know it is5: // a quit message.6: mQueue.enqueueMessage(msg, 0);7: }
上面quit 的源码的注释也解释了,如果消息没有target,就是结束Looper 的消息。
最后,关于Handler,就说这么多,希望上面的一些讲解对大家有帮助。