一. 相关概念
1. Message
1) 消息对象,就像是盛放消息的容器,Message Queue中的存放的对象。
2) 从MessagePool中获取Message对象有两种方法:Message.obtain()和handler.obtainMessage()。
3) 获取消息对象时并不一定是直接创建一个新的实例,而是先从Message Pool(消息池)中看有没有可用的Message实例,存在则直接取出返回这个实例。如果Message Pool中没有可用的Message实例,则才用给定的参数创建一个Message对象
2. MessageQueue
1) 消息队列,存放消息的队列。
2) 一个线程最多可有一个MessageQueue。
3) 每一个MessageQueue都不能脱离Looper而存在,Looper是消息的管理者。
4) 除了主线程,在创建其他线程的时候,并不会自动创建其MessageQueue。通常新建一个Looper对象,然后由Looper.prepare()创建MessageQueue并对该线程的MessageQueue进行管理。
3. MessagePool
1) 线程池,存放空的消息Message。当Message中有消息时放在MessageQueue中,当没消息时就会被放入MessagePool中。
2) 存储结构为链表。
3) 默认线程池的大小是10,即可以存放最多10空的message。
4) 调用Handler.removeMessages()时,将Message从MessageQueue中删除,同时放入到Message Pool中。
5) 当消息分发完成后调用Message.recycle()回收消息,将其放入Message Pool中。
4. Looper
1) 循环(暂且理解为),MessageQueue的管理者。
2) 一个线程拥有一个Looper。主线程默认会创建一个Looper,Looper.getMainLooper () 获得主线程(UI线程)的Lopper。其他线程则通过 Looper.prepare()创建该线程的Looper对象;通过looper.myLooper()获得该线程的Looper对象。
3) 主线程自动创建一个Looper对象的同时也自动创建MessageQueue,其他线程通过Looper.prepare()创建该线程的MessageQueue。
4) 调用 Looper.loop()启动消息循环,之后就可以发消息、取消息、和处理消息等操作。
5) 当Looper对象看到MessageQueue中含有Message,就将其广播出去。该handler 对象收到该消息后,调用相应的handler 对象的handleMessage()方法,对其进行处理。
6) Looper负责从MessageQueue中取出消息,并且分发到消息指定目标Handler对象。
7) 退出时还要释放资源,调用Looper.release()。
5. Handler
1) 消息的处理者(只处理由自己发出的消息)。发消息,收消息,处理消息,移除消息(从MessageQueue中)。
2) 一个线程可以有多个handler,但只能有一个Looper。
3) Handler.obtainMessage()将消息封装成Message.
4) Handler.sendMessage()将消息发送给指定的Handler,继而由Looper将Message放入MessageQueue中。
5) Handler.handleMessage()接收消息,处理消息。
6) sendMessage()与handleMessage()的handler对象为同一个handler。若一个线程中有多个handler,如handlerA,handlerB,即使共同操纵同一个MessageQueue,它们也是不能进行通信的,handlerA得不到handlerB的消息。
7) Handler都是在主线程中的,也就是说,Handler类就都在主线程中运行,通俗的说就是“绑定”在主线程中,而不属于创建它的子线程。
8) 调用Handler.dispatchMessage()/msg.target.dispatchMessage(msg)分发消息。
9) 使用Handler对象的sendEmptyMessage或sendMessage方法来传递一个Bundle对象到Handler处理器。
10) Handler.removeMessages(),将handler对应MessageQueue里的message删除,同时放入到MessagePool中;如果带了int参数则是对应的消息清空。队列里面没有消息则handler会不工作,但不表示handler会停止。
二. 消息传递机制
1、 初始化Looper
2、 绑定handler到Looper对象
3、 定义处理消息的方法
4、 启动消息循环
三. 备注
1. 子线程不能操纵主线程的控件。
2. Toast这种机制是不合view相关的,也不和activity相关的,不像dialog,取决于创建它的activity,Toast是由一种称为INotificationManager的服务管理的,所以虽然视图A虽然没有获取焦点,但是视图A对象仍旧在栈中,依旧存在,handlerA对象也存在,所以当他的到消息的时候,他依旧会去处理,弹出toast,Toast是一种很特别的机制,使用的时候一定要小心。
3. 要给哪个线程发消息,就要用该线程的Looper来创建handler,目的是创建消息队列MessageQueue。利用其handler来向其消息队列发送message
4. 处理消息时一定不要忘了开启消息循环,Looper.loop()。这样才能接收到多个消息。
5. 线程安全和线程不安全线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据
四. 参考资源
1. http://www.eoeandroid.com/forum.php?mod=viewthread&tid=49595
2. http://blog.sina.com.cn/s/blog_49867dc001012ojl.html
3. http://blog.sina.com.cn/s/blog_629b701e0100rf45.html
这些博客资源对我受益匪浅,希望对大家有所帮助!
五. 源码
下载地址:http://download.youkuaiyun.com/detail/u013061822/9019737
六. 代码编写出错点
1. 在实现主线程向子线程发送消息时,不能正确显示收到的消息。打断点跟踪调试,发现子线程可以正确的收到消息,并把该消息发送给主线程,但是UI中textview中却没有显示收到的消息。推测可能是没有循环消息队列。添加Looper.loop()后正确显示!
2. 由第一个错误后,我把项目中每一个线程run()方法中都添加了Looper.loop()语句,当点击第二个按钮子线程向主线程发送消息时出错:08-18 14:44:10.363: E/AndroidRuntime(26101):java.lang.RuntimeException: No Looper; Looper.prepare() wasn't called on thisthread.出错截图:
大概是Looper为空,原来当子线程没有创建其本身的Looper时,是不能随便添加Looper.loop(),不能随便开启消息循环。如果一个线程需要接收消息的话则可以开启消息循环,如果只需要发送消息则不能开启消息循环队列。