Handler详解系列(一)——Handler异步消息机制详解(附图)

MainActivity如下:

package cc.cn;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.app.Activity;

/**
 * Demo描述:
 * Android异步消息机制分析(附图)
 *
 * =======================================================
 *
 * 问题的引入:
 * 在子线程中直接调用Handler handler=new Handler()此时报错:
 * Can't create handler inside thread that has not called Looper.prepare().
 * 既然是在调用Handler的构造方法时报的错那就去看该构造方法的源码.
 * 	public Handler() {
 *		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 = null;
 *	}
 *
 * 从以上代码mLooper = Looper.myLooper()可以看到当mLooper==null时就会报错
 * Can't create handler inside thread that has not called Looper.prepare().
 *
 *
 * 继续看Looper.myLooper()的源码.
 * *Return the Looper object associated with the current thread.
 * *Returns null if the calling thread is not associated with a Looper.
 *  public static Looper myLooper() {
 *       return sThreadLocal.get();
 *   }
 * 请看源码中的这两行注释:
 * 返回与当前线程相关联的Looper.
 * 如果当前线程没有一个与之相关联的Looper那么就返回null.
 *
 * 回看刚才的报错Can't create handler inside thread that has not called Looper.prepare().
 * 提示我们调用Looper.prepare().
 * 于是在子线程中这么写:
 * Looper.prepare();
 * Handler handler=new Handler();
 * 该报错消失.
 *
 * 既然调用Looper.prepare()可以消除该错误.
 * 那就看看Looper.prepare()的源码.
 *
 *  /** Initialize the current thread as a looper.
 *  * This gives you a chance to create handlers that then reference
 *  * this looper, before actually starting the loop. Be sure to call
 *  * {@link #loop()} after calling this method, and end it by calling
 *  * {@link #quit()}.
 *  public static void prepare() {
 *      if (sThreadLocal.get() != null) {
 *         throw new RuntimeException("Only one Looper may be created per thread");
 *       }
 *       sThreadLocal.set(new Looper());
 *  }
 *  1 从注释文档可以看出该prepare()方法的作用:
 *    利用一个looper来初始化当前线程.
 *  2 在调用loop()方法前必须调用该prepare()方法.
 *  3 注意该方法的异常提示:
 *    Only one Looper may be created per thread
 *    每一个线程只有一个Looper!!!!
 *
 *  在该方法的代码:sThreadLocal.set(new Looper());
 *  Looper构造方法如下:
 *  private Looper() {
 *    mQueue = new MessageQueue();
 *    mRun = true;
 *    mThread = Thread.currentThread();
 *  }
 *  可以看到为sThreadLocal保存了一个Looper.
 *  所以在上述的myLooper()方法中调用sThreadLocal.get()就不再为空了.
 *
 *
 *  至此一个子线程中使用Handler的方式应该是这样的:
 *  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();
 *      }
 *  }
 *  该代码也是Google推荐的写法.
 *
 *
 *  在平常使的MainActivity中的UI线程中使用Handler时并没有调用Looper.prepare();
 *  这是为什么呢?
 *  因为UI线程是主线程,系统已经自动帮我们调用了Looper.prepare()方法.
 *  在此不再赘述.
 *
 *
 *
 *  以上讨论了线程(Thread)和Looper的使用,下面讨论消息的发送和处理过程.
 *  平常最常用的方式:
 *  handler.sendMessage(message);
 *  那么handleMessage(Message msg)中是怎么获取到刚发出的这条消息呢???
 *  Handler发送消息的具体方法有好几个但除了sendMessageAtFrontOfQueue(Message msg)
 *  以外的几个方法最终会调用sendMessageAtTime(Message msg, long uptimeMillis)方法:
 *
 *  public boolean sendMessageAtTime(Message msg, long uptimeMillis){
 *      boolean sent = false;
 *      MessageQueue queue = mQueue;
 *      if (queue != null) {
 *          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;
 *  }
 *
 *  在该方法中有两句代码很重要:
 *  1 msg.target = this;
 *    给msg设置了target.
 *    这里的this当然就是当前Handler对象本身!
 *  2 sent = queue.enqueueMessage(msg, uptimeMillis);
 *    将消息放入消息队列中.
 *    这里的queue(mQueue)就是上述Looper构造方法中的mQueue.
 *    在enqueueMessage(msg, uptimeMillis)方法中有一个队列.
 *    距离触发时间最短的message排在队列最前面,同理距离触发时间最长的message排在队列的最尾端.
 *    若调用sendMessageAtFrontOfQueue()方法发送消息它会调用该enqueueMessage(msg, uptimeMillis)
 *    来让消息入队只不过时间为延迟时间为0,即它会插入到队列头部.
 *
 *
 *  这就是消息的入队操作,那么消息怎么出队呢?
 *  这就要看Looper中的loop()方法
 *  public static final void loop() {
 *    Looper me = myLooper();
 *    MessageQueue queue = me.mQueue;
 *    while (true) {
 *       Message msg = queue.next(); // might block
 *       if (msg != null) {
 *          if (msg.target == null) {
 *              return;
 *          }
 *          if (me.mLogging!= null) me.mLogging.println(
 *          ">>>>> Dispatching to " + msg.target + " "+ msg.callback + ": " + msg.what);
 *          msg.target.dispatchMessage(msg);
 *          if (me.mLogging!= null) me.mLogging.println(
 *          "<<<<< Finished to    " + msg.target + " "+ msg.callback);
 *          msg.recycle();
 *      }
 *     }
 *   }
 *   在该方法中是一个死循环while(true),即Looper一直在轮询消息队列(MessageQueue)
 *   在该方法中有两句代码很重要:
 *   1 Message msg = queue.next();
 *     queue.next()消息队列的出列.
 *   2 msg.target.dispatchMessage(msg);
 *     用调用msg里的target的dispatchMessage()方法.
 *     target是什么呢?
 *     参见上述sendMessageAtTime(Message msg, long uptimeMillis)可知:
 *     target就是Handler!!!!在此回调了Handler的dispatchMessage方法,所以该消息就发送给了对应的Handler.
 *     接下来看Handler的dispatchMessage(Message msg)方法:
 *
 *  public void dispatchMessage(Message msg) {
 *       //1 message的callback
 *    if (msg.callback != null) {
 *       handleCallback(msg);
 *    } else {
 *       //2 handler的callback
 *       if (mCallback != null) {
 *           if (mCallback.handleMessage(msg)) {
 *               return;
 *          }
 *      }
 *      //3 Handler的handleMessage()
 *      handleMessage(msg);
 *    }
 *  }
 *
 * 其中涉及到的CallBack为:
 * public interface Callback {
 *    public boolean handleMessage(Message msg);
 * }
 * Handler的其中一个构造方法为:
 * Handler handler=new Handler(callback);
 * 所以在dispatchMessage(Message msg)涉及到了CallBack
 * 在绝大多数情况下message和Handler的callBack均为空.
 * 所以会调用dispatchMessage(Message msg)方法:
 * 这就回到了我们最熟悉的地方.
 *
 *
 *
 * =======================================================
 *
 * Android异步消息机制中主要涉及到:
 * Thread Handler Looper MessageQueue
 * 它们的相互关系如下:
 * 1 Looper.prepare();
 *   1.1 为当前线程生成对应的Looper.
 *       一个Looper包含了三个变量:
 *       mQueue---->消息队列(MessageQueue)
 *       mRun------>true
 *       mThread--->当前线程
 *   1.2 将该Looper保存到ThreadLocal中.
 *       之所以采用ThreadLocal来存放线程对应的Looper主要目的是确保
 *       每个线程只有一个唯一的Looper.
 *
 *   Looper和其所属线程的相互关联的代码体现:
 *   1.3 Looper.myLooper()
 *       得到与当前线程相关联的Looper
 *   1.4 Looper.myLooper().getThread()
 *       得到与Looper相关联的线程Thread
 *
 *  2 Handler handler=new Handler()
 *    在第一步中利用Looper.prepare()实现了Looper与线程的关联.
 *    在此接着看Handler与Looper的关系.
 *    注意看上述Handler的构造方法中的代码:
 *    mLooper = Looper.myLooper();
 *    这样就得到了与线程相关联的Looper.
 *    mQueue = mLooper.mQueue;
 *    得到与线程相关联的Looper的消息队列(MessageQueue)mQueue
 *    在Handler的构造方法中就看出了Handler与Looper的关联.
 *
 *  小结:
 *  (1) Looper.prepare();
 *      实现了Looper和其所属线程的相互关联
 *  (2) Handler handler=new Handler()
 *      实现了Handler与Looper的关联.
 *  (3) 一个线程可有多个Handler但只有一个Looper
 *
 *
 * =======================================================
 * 了解Thread Handler Looper MessageQueue几者间的关系之后,在此总结
 * Android异步消息机制的流程:
 *
 * 1 Looper.prepare();
 *   实现Looper和其所属线程的相互关联
 *   其中Looper包含了三个变量:
 *   mQueue---->消息队列(MessageQueue)
 *   mRun------>true
 *   mThread--->当前线程
 *   在执行该方法以后每个Looper中就存在一个消息队列(MessageQueue)了.
 *   Handler发送的消息都会保存到该消息队列(MessageQueue)中.
 * 2 Handler handler=new Handler();
 *   实现了Handler与Looper的关联.
 * 3 Looper.loop();
 *   开始轮询消息队列(MessageQueue),并且会一直轮询.
 *   按照队列处理里面的每个消息.当然刚开始的时候该队列为空.
 * 4 mHandler.sendMessage(message)发送消息至消息队列.
 *   具体过程可以参见以上的详细描述.
 * 5 在第三步时Looper.loop()不是一直在轮询消息队列么?
 *   当消息(Message)出队时,找到该消息的target(其实就是一个Handler)回调
 *   其dispatchMessage(Message msg)方法;在该方法中会调用到我们非常熟悉
 *   的handleMessage(Message msg).
 *
 * 以上就是Android异步消息机制的详细流程.
 * 简单地可以这么说:
 * Handler------>发送消息和处理消息
 * Looper------->用消息队列(MessageQueue)保存消息.使用loop()方法一直轮询消息队列.
 *               并在消息出队的时候将其发送给合适的Handler.
 *
 * 以上流程分析可参见流程图.
 *
 * =======================================================
 * 参考资料:
 * 1 http://androidxref.com/4.0.4/
 * 2 http://blog.csdn.net/guolin_blog/article/details/9991569
 * 3 http://blog.csdn.net/aaa2832/article/details/7773220
 * 4 http://blog.csdn.net/lilu_leo/article/details/8145320
 * 5 http://blog.csdn.net/oney139/article/details/7922742
 * 6 http://www.cnblogs.com/cqcmdwym/archive/2013/05/12/3074138.html
 *   Thank you very much
 */
public class MainActivity extends Activity {
	private Thread mThread;
	private Handler mHandler;
	private final String TAG = "Handler";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		testHandler();
	}

	private void testHandler() {

		mThread = new Thread(new Runnable() {
			@Override
			public void run() {
				Looper.prepare();

				mHandler = new Handler() {
					@Override
					public void handleMessage(Message msg) {
						super.handleMessage(msg);
						if (msg.what == 123) {
							Log.i(TAG, "收到消息  msg.arg1=" + msg.arg1);
						}
					}
				};

				Message message = new Message();
				message.what = 123;
				message.arg1 = 456;
				mHandler.sendMessage(message);
				Looper.loop();
			}
		});
		mThread.start();
	}

}

附图如下:

main.xml如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   >

    <TextView
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Handler原理学习" />

</RelativeLayout>

时间: 2024-10-07 15:24:23

Handler详解系列(一)——Handler异步消息机制详解(附图)的相关文章

Android Handler和他的小伙伴们,消息机制详解

Handler一直是面试很热的话题,最近又看了好多文章,下面结合源码来总结一下. Handler 是Android 消息机制的上层接口,Handler的运行需要底层的MessageQueue和Looper的支撑,他们是Handler的好基友.Handler的运行机制也就是Android的消息机制. 我们都知道Handler是用来更新UI的,其实更新UI只是开发者最常用的场景.概括来讲:有时候需要在子线中进行耗时较长的I/O操纵,而I/O操作完成后需要在UI上做一些改变,这个时候可以通过Handl

[转]Handler消息机制详解

能简单说得我们尽量不复杂: 为了避免ANR,我们会通常把 耗时操作放在子线程里面去执行,因为子线程不能更新UI,所以当子线程需要更新的UI的时候就需要借助到安卓的消息机制,也就是Handler机制了. 注意:在安卓的世界里面,当 子线程 在执行耗时操作的时候,不是说你的主线程就阻塞在那里等待子线程的完成--也不是调用 Thread.wait()或是Thread.sleep().安卓采取的方法是,主线程应该为子线程提供一个Handler,以便完成时能够提交给主线程.以这种方式设计你的应用程序,将能

Android异步消息处理机制详解及源码分析

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果] 最近相对来说比较闲,加上养病,所以没事干就撸些自己之前的知识点为博客,方便自己也方便别人. 1 背景 之所以选择这个知识点来分析有以下几个原因: 逛GitHub时发现关注的isuss中有人不停的在讨论Android中的Looper , Handler , Me

拨云见日---android异步消息机制源码分析

做过windows GUI的同学应该清楚,一般的GUI操作都是基于消息机制的,应用程序维护一个消息队列,开发人员编写对应事件的回调函数就能实现我们想要的操作 其实android系统也和windows GUI一样,也是基于消息机制,今天让我们通过源码来揭开android消息机制的神秘面纱 谈起异步消息,就不能不提及Handler,在安卓中,由于主线程中不能做耗时操作,所以耗时操作必须让子线程执行,而且只能在主线程(即UI线程)中执行UI更新操作,通过Handler发送异步消息,我们就能更新UI,一

Android 源码分析(四) Handler 异步消息机制

一.Handler 使用方法: Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 1: Log.i(TAG,"UI thread sendEmptyMessage...1"); break; case 2: Log.i(TAG,"thread sendEmp

Windows消息机制详解

消息是指什么?      消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉.一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向 Windows发出一个通知,告诉应用程序某个事情发生了.例如,单击鼠标.改变窗口尺寸.按下键盘上的一个键都会使Windows发送一个消息给应用程序. 消息本身是作为一个记录传递给应用程序的,这个记录中包含了消息的类型以及其他信息.例如,对于单击鼠标所产生的消息来说,这个记录中包含了单击鼠标时的坐标.这个记录类型叫做MSG,MSG含有来

Struts2之异步调用机制详解

一.学习案例:通过在getXML.jsp页面改变名称,在不刷新当前页面的情况下修改页面的数据. 二.案例分析:struts和ajax配合,主要是返回数据的定义.我们可以返回json和xml格式的数据.在此只演示xml方式. a)格式xml数据,一是拼接字符串,但是数据多的情况下,代码维护就不行了.所以在此我们借助dom4j.把下载的dom4j.jar放到lib目录下. b)创建scripts包,把jquery.js放进去. c)创建getXML.jsp页面,编写html以及jquery. d)创

Handler详解系列(五)——Handler的post()方法详解

MainActivity如下: package cc.testui1; import android.os.Bundle; import android.os.Handler; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; import android.app.Activity; /*

Windows 消息机制详解

总的来说: MSG包括: 窗口句柄,指示MSG发送的目的窗口 消息标识 lPARAM.wParam 发送时间 发送时的鼠标位置   关于消息队列: Windows系统有一个系统消息队列 每个线程都有一个自己的消 息队列(由于发送消息MSG需 要提供一个窗口HWnd,而基 本有窗口的线程,都是UI线 程),因此基本上如果线程使用了GDI函数,则windows给该线程分配一个线程消息队列,这个消息队列负责该线程的所有窗口的消息.   所有的窗口都有自己的句柄(HWND),消息被发送时,这个句柄就已经