举个栗子:通过网络获取数据然后显示在TextView中,由于网络通信属于耗时操作,所以必须在子线程中完成,但是子线程中是不能更新UI的(特殊情况除外),为了解决以上问题,Android引入了Handler机制,由Handler来负责与子线程进行通信,从而使子线程与主线程之间建立起协作的桥梁,使Android的UI更新问题得到完美的解决。
注:Android系统是在onResume方法回调之后检查当前线程的,在此之前是可以在子线程中更新UI的,Google并不建议这么做,了解就好。
看图说话:
先说说几个重要的类:
Message:消息,由MessageQueue统一队列,然后交由Handler处理。
MessageQueue:消息队列,用来存放Handler发送过来的Message,并且按照先入先出的规则执行。
Handler:处理者,负责发送和处理Message。
Looper:消息轮询器,不断的从MessageQqueue中抽取Message并执行。
在创建Activity之前,当程序启动的时候,系统会先加载ActivityThread这个类,在这个类的main函数中,调用Looper.prepareMainLooper()初始化Looper对象并创建消息队列,然后调用Looper.loop()方法,不断的轮询消息队列中的消息。
package android.app; public final class ActivityThread { public static void main(String[] args) { ... // 初始化Looper对象并创建消息队列 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")); } Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); // 开启消息轮询 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); } }看下Looper.prepareMainLooper()与Looper.loop()方法中发生了什么:
package android.os; public final class Looper { /** * 为当前线程初始化Looper对象并创建消息队列 * 消息循环可以被终止 */ public static void prepare() { prepare(true); } /** * 为当前线程初始化Looper对象并创建消息队列 */ 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)); } /** * 为UI线程初始化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(); } } /** * 启动消息轮询 */ 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(); if (msg == null) { return; } try { // 处理消息 msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } // 回收消息 msg.recycleUnchecked(); } } /** * 获取当前线程的Looper对象 */ public static @Nullable Looper myLooper() { return sThreadLocal.get(); } /** * 获取当前线程的消息队列 */ public static @NonNull MessageQueue myQueue() { return myLooper().mQueue; } /** * Looper构造方法,初始化消息队列与当前线程 */ private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } }到这里,Handler发送和接收消息的准备工作就已经完成了,接下来让我们来初始化一个Handler试试吧!
通常我们都会在主线程中创建Handler来接收子线程的消息,看下Handler是如何创建的:
// 定义一个Handler对象,并实现handleMessage方法 Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); // 在此接收子线程发送的消息 } };Handler中的构造方法最终都会调用Handler(Callback callback, boolean async)方法,获取当前线程的Lopper对象,与之关联,然后获取消息队列。
package android.os; public class Handler { public Handler(Callback callback, boolean async) { ... // 获取当前线程的Looper对象 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } // 获取消息队列 mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } }看下enqueueMessage方法:
boolean enqueueMessage(Message msg, long when) { ... synchronized (this) { ... msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; // 如果有新消息首次加入队列或者新消息的延迟时间小于队列中首个消息的延迟时间,就将新消息放在队列头部 if (p == null || when == 0 || when < p.when) { msg.next = p; mMessages = msg; needWake = mBlocked; } else { // 如果队列不为空,将新消息按照时间排序放入队列 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; prev.next = msg; } if (needWake) { nativeWake(mPtr); } } return true; }发送消息到消息队列后,Looper就会在消息队列中按顺序取出消息分发给Handler处理,看下dispatchMessage方法,如果callback对象为null的话,就会回调handleMessage方法,如果不为空会回调callback的run方法。我们平时使用的sendMessage方法没有设置callback,所以会回调handleMessage方法,如果使用post(Runnable callback)方法,则会回调callback的run方法。
package android.os; public class Handler { /** * 消息分发 */ public void dispatchMessage(Message msg) { if (msg.callback != null) { // 回调callback的run方法 handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } // 回调handleMessage方法 handleMessage(msg); } } }OK,通过对源码的分析,Handler的原理已经学习完了,接下来让我们来学习一下如何在子线程中创建Handler。
通常我们使用Handler都是从子线程向主线程发送消息,如果需要主线程通知子线程做一些耗时逻辑,或者子线程之间进行通信的话,直接在子线程中创建Handler会抛出异常:
Can't create handler inside thread that has not called Looper.prepare()看下源码:
package android.os; public class Handler { public Handler(Callback callback, boolean async) { ... // 获取当前线程的Looper对象 mLooper = Looper.myLooper(); // 如果looper为空则会抛出异常 if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } // 获取消息队列 mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } }由于每个线程都用自己的Looper,这个Looper可以为null,系统默认在主线程中创建了Looper,但在子线程中需要手动设置,否则就会抛出异常。
代码实现:
new Thread("子线程") { @Override public void run() { super.run(); // 初始化Looper对象并创建消息队列 Looper.prepare(); Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.i(TAG, (String) msg.obj + "___" + Thread.currentThread().getName()); } }; Message message = new Message(); message.obj = "消息"; handler.sendMessage(message); // 开始轮询 // 由于loop方法是死循环,所以要写在最后 Looper.loop(); } }.start();还有没有更优雅的方式呢?答案是肯定的,为了解决上面的问题,Android系统为我们提供了HandlerThread类,看下源码:
package android.os; public class HandlerThread extends Thread { ... @Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { // 初始化Looper对象并创建消息队列 mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); // 开启消息轮询 Looper.loop(); mTid = -1; } }有没有很熟悉,HandlerThread类的run方法中做了和ActivityThread类中一样的处理,这样就不用再手动初始化Looper了,nice,接下来让我们用HandlerThread来实现一下:
HandlerThread handlerThread = new HandlerThread("HandlerThread"); handlerThread.start(); Handler handler = new Handler(handlerThread.getLooper()){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.i(TAG, (String) msg.obj + "___" + Thread.currentThread().getName()); } }; Message message = new Message(); message.obj = "消息"; handler.sendMessage(message);欢迎同学们吐槽评论,如果你觉得本篇博客对你有用,那么就留个言或者顶一下吧(^-^)