Android Handler消息机制深入浅出
尊重原创:http://blog.csdn.net/yuanzeyao/article/details/38408493
作为Android开发人员,Handler这个类应该是再熟悉不过了,因为几乎任何App的开发,都会使用到Handler这个类,有些同学可能就要说了,我完全可以使用AsyncTask代替它,这个确实是可以的,但是其实AsyncTask也是通过Handler实现的,具体的大家可以去看看源码就行了,Handler的主要功能就是实现子线程和主线程的通信,例如在子线程中执行一些耗时操作,操作完成之后通知主线程跟新UI(因为Android是不允许在子线程中跟新UI的)。
下面就使用一个简单的例子开始这篇文章吧
- public class MainActivity extends Activity
- {
- public static final int MSG_DOWNLOAD_FINISH=0X001;
- //创建一个Handler的匿名内部类
- private Handler handler=new Handler()
- {
- @Override
- public void handleMessage(Message msg)
- {
- switch(msg.what)
- {
- case MSG_DOWNLOAD_FINISH:
- Log.v("yzy", "handler所在的线程id是-->"+Thread.currentThread().getName());
- break;
- }
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- }
- //启动一个下载线程
- public void download(View view)
- {
- new Thread()
- {
- public void run() {
- try
- {
- Log.v("yzy", "下载子线程的Name是--->"+Thread.currentThread().getName());
- //在子线程运行,模拟一个下载任务
- Thread.sleep(2000);
- //下载完成后,发送下载完成消息
- handler.sendEmptyMessage(MSG_DOWNLOAD_FINISH);
- } catch (InterruptedException e)
- {
- e.printStackTrace();
- }
- };
- }.start();
- }
运行结果:
08-03 16:31:46.418: V/yzy(30486): 下载子线程的Name是--->Thread-22588
08-03 16:31:48.421: V/yzy(30486): handler所在的线程Name是-->main
上面例子就是模拟了一个下载任务,当下载完成后,通过Handler对象发送一个消息,最后被handlerMessage方法接收并处理,通过运行结果可以发现,download方法是在子线程中完成的,而handlerMessage是在UI线程中被调用的。到了这里一个简单的Handler使用案例就结束了,如果你已经明白了上面的代码,那么说明你已经明白了Handler的最基本使用。
下面再来一个简单的例子:
- public void postRun(View view)
- {
- new Thread(){
- public void run() {
- try
- {
- Log.v("yzy", " 下载子线程的Name是--->"+Thread.currentThread().getName());
- Thread.sleep(2000);
- handler.post(new Runnable()
- {
- @Override
- public void run()
- {
- Log.v("yzy", "handler post run -->"+Thread.currentThread().getName());
- }
- });
- } catch (InterruptedException e)
- {
- e.printStackTrace();
- }
- };
- }.start();
- }
运行结果:
08-03 16:31:52.528: V/yzy(30486): 下载子线程的Name是--->Thread-22589
08-03 16:31:54.531: V/yzy(30486): handler post run -->main
在Handler中除了可以发送Message对象还可以发送Runnable对象,从运行结果来看发送的Runnable也是在main线程中执行的
那么是不是通过handler发出的Message和Runnable都是在UI线程中执行的呢,这个可不一定,现在我在onCreate方法中创建一个handler2
- HandlerThread thread=new HandlerThread("yzy");
- thread.start();
- handler2=new Handler(thread.getLooper())
- {
- public void handleMessage(Message msg) {
- switch(msg.what)
- {
- case MSG_DOWNLOAD_FINISH:
- Log.v("yzy", "handler所在的线程Name是-->"+Thread.currentThread().getName());
- break;
- }
- };
- };
然后将上面代码中使用handler的地方换为handler2,然后运行代码:代码中创建了一个名称为"yzy"的线程,然后再yzy线程中初始化了handler2
结果如下:
08-03 17:07:10.571: V/yzy(8224): 下载子线程的Name是--->Thread-23005
08-03 17:07:12.574: V/yzy(8224): handler所在的线程Name是-->yzy
我们发现通过handler2发出的Message和Runnable都是在yzy线程中执行的。
如果对于上面的代码和运行结果你都知道原因,那么说明你已经对Handler的使用非常熟悉了,如果不清楚上面的运行结果,那么请往下继续吧
开始分析原因之前我想提出以下几个问题:
1、Handler发出的Message和Runnable是如何传递到UI线程的?
2、在什么情况下通过Handler发出的Message和Runnable不会传递到UI线程?
3、为什么我是在HandlerThread初始化一个Handler?
下面我们就逐一解决上述问题吧,在解决问题之前我们需要分析一个Hanlder这个类的源码。
首先看看Handler的构造函数,它有好几个构造函数,我们一个个的看吧
- //无参构造函数
- public Handler() {
- //检查Handler是否是static的,如果不是的,那么有可能导致内存泄露,具体可以百度
- if (FIND_POTENTIAL_LEAKS) {
- final Class<? extends Handler> klass = getClass();
- if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
- (klass.getModifiers() & Modifier.STATIC) == 0) {
- Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
- klass.getCanonicalName());
- }
- }
- //很重要的一个属性,暂时就将Looper理解成一个消息队列的管理者吧,用来从消息队列中取消息的,稍后会分析这个类
- mLooper = Looper.myLooper();
- if (mLooper == null) {
- //这个异常非常常见哦,Handler一定要在有Looper的线程上执行,这个也就是为什么我在HandlerThread中初始化Handler
- //而没有在Thread里面初始化,如果在Thread里面初始化需要先调用Looper.prepare方法
- throw new RuntimeException(
- "Can't create handler inside thread that has not called Looper.prepare()");
- }
- //将mLooper里面的消息队列复制到自身的mQueue,这也就意味着Handler和Looper是公用一个消息队列
- mQueue = mLooper.mQueue;
- //回调函数默认是Null
- mCallback = null;
- }
- //这个和上面一样的,只不过传入了一个回调函数
- public Handler(Callback callback) {
- if (FIND_POTENTIAL_LEAKS) {
- final Class<? extends Handler> klass = getClass();
- if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
- (klass.getModifiers() & Modifier.STATIC) == 0) {
- Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
- klass.getCanonicalName());
- }
- }
- 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;
- }
- /**
- * 传入一个Looper,并和Looper公用一个消息队列
- */
- public Handler(Looper looper) {
- mLooper = looper;
- mQueue = looper.mQueue;
- mCallback = null;
- }
- /**
- * 和上面一个差不多,就不在赘述
- */
- public Handler(Looper looper, Callback callback) {
- mLooper = looper;
- mQueue = looper.mQueue;
- mCallback = callback;
- }
看了Handler的构造函数,发现Hanlder里面有一个消息队列,这个消息队列是从Looper里面传入的,并且Handler需要在有Looper的线程中调用。既然它和Looper关系这么紧密,我们看看Looper到底是什么东西吧,打开Looper的源码,顶部看到这样的一个例子:
- * class LooperThread extends Thread {
- * public Handler mHandler;
- *
- * public void run() {
- * Looper.prepare();
- *
- * mHandler = new Handler() {
- * public void handleMessage(Message msg) {
- * // process incoming messages here
- * }
- * };
- *
- * Looper.loop();
- * }
- *
这个就是为了说明Handler只能在有Looper的线程中创建,UI线程是有Looper的,对于没有Looper的线程,需要调用Loope.prepare,Looper.loop等函数,如上例。
那我们正式进入Looper的源码看看吧
- //Looper的构造函数,主要是对消息队列等属性初始化
- private Looper() {
- mQueue = new MessageQueue();
- mRun = true;
- //记录所在线程
- mThread = Thread.currentThread();
- }
- //在上面的例子中,看到当在一个没有Looper的线程中创建Handler,就需要执行这个函数,
- //这个函数主要是new 一个Looper,燃火放入ThreadLocal中保存,做到一个线程就创建一次。第二次调用这个方法会抛出异常的
- public static final void prepare() {
- if (sThreadLocal.get() != null) {
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- sThreadLocal.set(new Looper());
- }
- //prepare是创建并保存,这个方法就是取出Looper
- public static final Looper myLooper() {
- return (Looper)sThreadLocal.get();
- }
- //这个方法是给系统调用的,UI线程通过调用这个线程,从而保证UI线程里有一个Looper
- //需要注意:如果一个线程是UI线程,那么myLooper和getMainLooper是同一个Looper,通过这个代码很好理解
- public static final void prepareMainLooper() {
- prepare();
- setMainLooper(myLooper());
- if (Process.supportsProcesses()) {
- myLooper().mQueue.mQuitAllowed = false;
- }
- }
- //获得UI线程的Looper,通常我想Hanlder的handleMessage在UI线程执行时通常会new Handler(getMainLooper());
- public synchronized static final Looper getMainLooper() {
- return mMainLooper;
- }
- /**
- * 上面的例子是执行了这个方法的,这个方法是一个死循环,一直从消息队列中读取消息,并分发出去
- */
- public static final void loop() {
- //得到本线程的Looper
- Looper me = myLooper();
- //拿到消息队列
- MessageQueue queue = me.mQueue;
- while (true) {
- //从消息队列中拿一个消息
- Message msg = queue.next(); // might block
- //取到了一个消息
- if (msg != null) {
- //这个一般不等于Null,通常就是发出这个Message的Handler
- if (msg.target == null) {
- // No target is a magic identifier for the quit message.
- return;
- }
- if (me.mLogging!= null) me.mLogging.println(
- ">>>>> Dispatching to " + msg.target + " "
- + msg.callback + ": " + msg.what
- );
- //调用Handler的dispatchMessage方法
- msg.target.dispatchMessage(msg);
- if (me.mLogging!= null) me.mLogging.println(
- "<<<<< Finished to " + msg.target + " "
- + msg.callback);
- //将消息放入消息池
- msg.recycle();
- }
- }
- }
- //返回Looper的消息队列
- public static final MessageQueue myQueue() {
- return myLooper().mQueue;
- }
看了Looper的源码之后总结一下:
在一个线程的run方法里面先调用prepare方法,主要保证了该线程里面有了一个Looper对象,如果在这个线程里面创建一个Hanlder,那么看看上面Handler的空的构造方法,Handler里面的Looper就是线程里面保存的Looper,从而可以创建的Handler和线程公用一个Looper,也就是公用一个消息队列。说的更具体一点就是Hanlder和Looper所在的线程公用一个消息队列。最后调用loop方法,这个线程不断从消息队列取消息,然后调用Hanlder的dispatchMessage方法。
最后看看Handler的其他几个比较重要的方法吧
- //在Handler中发送一个消息到消息队列,类似的方法很多,我只选了这一个
- public boolean sendMessageAtTime(Message msg, long uptimeMillis)
- {
- boolean sent = false;
- MessageQueue queue = mQueue;
- if (queue != null) {
- //将target设置成了Handler
- msg.target = this;
- sent = queue.enqueueMessage(msg, uptimeMillis);
- }
- else {
- RuntimeException e = new RuntimeException(
- this + " sendMessageAtTime() called with no mQueue");
- Log.w("Looper", e.getMessage(), e);
- }
- return sent;
- }
- //这个方法是在loop方法中被调用的
- public void dispatchMessage(Message msg) {
- //首先检查msg中callback(是一个Runnable对象)是否为Null,如果不为null,则直接调用callback中的run方法
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- //检查Handler中的callback是否为空,如果不为空,则直接调用callback中的handleMessage,如果返回TRUE,则直接返回不在调用Handler中的handleMessage
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- //最后调用Handler中的handlerMessage
- handleMessage(msg);
- }
- }
在前面的例子中,我们曾经使用过Handler中的post方法,我们看看它是怎么实现的
- public final boolean post(Runnable r)
- {
- return sendMessageDelayed(getPostMessage(r), 0);
- }
- private final Message getPostMessage(Runnable r) {
- Message m = Message.obtain();
- m.callback = r;
- return m;
- }
- public final boolean sendMessageDelayed(Message msg, long delayMillis)
- {
- if (delayMillis < 0) {
- delayMillis = 0;
- }
- return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
- }
我想如果前面明白了的,这个就不用我说了的吧。
其实在Activity中有一个方法runOnUiThread,就是使用这个原理实现的,如下:
- //让Runnable在UI线程执行
- public final void runOnUiThread(Runnable action) {
- //如果当前线程不是UI线程,那么通过Handler转发到UI线程
- if (Thread.currentThread() != mUiThread) {
- mHandler.post(action);
- } else {
- action.run();
- }
- }
View里面也有类似的方法:
- public boolean post(Runnable action) {
- Handler handler;
- if (mAttachInfo != null) {
- handler = mAttachInfo.mHandler;
- } else {
- // Assume that post will succeed later
- ViewRoot.getRunQueue().post(action);
- return true;
- }
- return handler.post(action);
- }
原理是上面是一样的。
根据上面的学习,可以得出结论:
1、如果Handler是在UI线程创建的,那么Handler和UI线程共享消息队列,所以通过Hanlder发出的消息都是在UI线程中执行的
2、如果要让Handler发出的消息在子线程中执行,很简单,在创建Handler的时候传入子线程的Looper(通过Looper.prepare创建)
到这里相信在前面我提及的三个问题都已经有答案了吧,如果有没有明白的,欢迎留言。。。。

浙公网安备 33010602011771号