概览
* Message:消息。消息里面可包含简单数据、Object和Bundle,还可以包含一个Runnable(实际上可看做回调)。
* MessageQueue:消息队列,供Looper线程消费消息。
* Looper:用于循环处理Message,一个Thread结合一个Looper来实现消息循环处理。Android App的主线程包含了Looper。
* Handler:负责向当前Looper线程发送Message,并实现如何处理消息的回调,回调可放到Callback接口的实现中,也可以放在传递进去的Runnable中的run中。
消息处理流程
1. MainThread(一个Looper Thread)正在运行,线程中有MessageQueue可交互,并循环处理MessageQueue中的Message。
2. 在MainThread中创建一个Handler,handler与当前线程Looper的MessageQueue绑定。
3. 通过handler.sendMessage(Message msg)向MessageQueue发送消息,等候执行;通过handler.post(Runnable r)向MessageQueue发送一个空消息,该空消息附加了Runnable,等候执行。
4. MainThread轮询MessageQueue的Message,抛给Message对应的Handler执行。
Message
* 最好通过Message.obtain()来创建Message对象,从消息池里创建Message更高效。
源码分析
public final class Message implements Parcelable
{
public int what; // 用户定义的标识码
public int arg1; // 用来存储简单的数据,这样可以不使用Object/Bundle来做消息。
public int arg2;
public Object obj; // 对象型数据。
public Messenger replyTo;
Bundle data; // 复杂型消息数据
// Message的最终处理分两种情况:
Handler target; // 1)通过Message的target(Handler)处理消息,具体是Handelr实现handleMessage()。
Runnable callback; // 2)通过空消息的callback(Runnable)处理消息,具体是丢弃Message,然后直接调用run()。
private static final Object sPoolSync = new Object(); // 消息池用到的锁
private static Message sPool ; // 从消息池中取出的可用的消息对象。每取一次,这里就放置一个可用的。
private static int sPoolSize = 0; // 当前可用的消息对象数量
private static final int MAX_POOL_SIZE = 50 ; // 池存放最大量
Message next; // 实现链表式消息池
// 从消息池中取一个可用的消息对象
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
// 从现有Message复制并返回
public static Message obtain(Message orig) {
Message m = obtain();
m.what = orig.what;
m.arg1 = orig.arg1;
m.arg2 = orig.arg2;
m.obj = orig.obj;
m.replyTo = orig.replyTo;
if (orig.data != null) {
m.data = new Bundle(orig.data);
}
m.target = orig.target;
m.callback = orig.callback;
return m;
}
// 发送消息
public void sendToTarget() {
target.sendMessage(this); // 调用的是 handler 的 sendMessage()
}
// 消息回收
public void recycle() {
clearForRecycle(); // 清除Message对象的所有信息
synchronized (sPoolSync) {
if(sPoolSize < MAX_POOL_SIZE){
next = sPool; // 下一个可用Message是当前sPool(可用)
sPool = this ; // 当前可用的Message为正在释放清除的Message对象
sPoolSize++; // 可用对象数量递增
}
}
}
}
MessageQueue
* holding the list of messages to be dispatched by a Looper。
* 不能直接向Looper的MessageQueue添加Message,需要通过Handler。
* 可通过 Looper.myQueue()获取到当前线程的MessageQueue。
Handler
* 创建一个Handler就会绑定到当前Thread/MessageQueue,之后,Handler可以向MessageQueue发送Message和Runnable,等候轮询执行。
* 发送一个Message:sendMessage(Message m)。
* 发送一个Runnable:post(Runnable r) 。
public class Handler
{
// 自定义的Handler必须实现该方法,用于处理消息
public void handleMessage(Message msg){}
// 或者指定一个实现了Callback接口的类
public interface Callback {
public boolean handleMessage(Message msg);
}
// 构造函数
// 默认情况下,handler会关联到当前Thread的Looper,如果没有Looper,抛异常
public Handler(Looper looper,Callback callback/*相当于指定了handleMessage()函数*/,boolean async){
mLooper=looper; mQueue=looper.mQueue; mCallback =callback;mAsynchronous = async;
}
public final Message obtainMessage(){ return Message.obtain(this); } // 直接调用的Message的obtain
// ---------------------------------------------------------------
// 发送消息到MessageQueue
// handler.sendXXX均调用此方法
public boolean sendMessageAtTime(Message msg,long uptimeMillis) {
MessageQueue queue = mQueue;
return enqueueMessage(queue,msg,uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {msg.setAsynchronous(true);}
return queue.enqueueMessage(msg, uptimeMillis);
}
// ---------------------------------------------------------------
// 发送Runnable到MessageQueue
// 原理是把Runnable附加到一个空消息的callback上,当执行消息时,如果发现有callback,则执行callback。
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r),0);
}
private static getPostMessage(Runnable r,Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r; // 指定Message的callback为r。通过sendMessage的消息没有callback,包含callback的Message将会被其callback执行。
return m;
}
// ---------------------------------------------------------------
// 处理消息
// 消息在Looper中是通过调用这个函数来实现处理消息的
public void dispatchMessage(Message msg) {
if(msg.callback!=null){ handleCallback(msg); } // 在Runnable中处理
else {
handleMessage(msg); // 调用Handler处理消息的函数
}
}
private static void handleCallback(Message message) {
message.callback.run(); //消息被丢弃,并且直接调用run(),而不是新开线程。
}
}// end handler()
Looper
* 默认线程并没有包含可以循环处理消息的功能,Looper类可以帮助线程实现消息循环。
public final class Looper
{
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); // 线程本地变量,每个线程一个Looper。
final Message mQueue;
final Thread mThread;
private Looper(boolean quitAllowed){
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
// --------------------------------------------------------------------
// 准备。为当前线程创建一个Looper对象
static void prepare(boolean quitAllowed/*MessageQueue是否可退出*/) {
if(sThreadLocal.get()!=null){/*Exception,当前线程的Looper已存在,一个线程一个Looper*/}
sThreadLocal.set(new Looper(quitAllowed));
}
// --------------------------------------------------------------------
// 开始循环
public static void loop() {
final Looper me = myLooper();// 当前线程的Looper实例
final MessageQueue queue = me.mQueue;
for(;;){
Message msg = queue.next();
if(msg == null) { return;}
msg.target.dispatchMessage(msg); // 由Handler处理该消息
msg.recycle(); // 清理该消息,清空后返回消息池。
}//end for
}// end loop()
// --------------------------------------------------------------------
public void quit() { mQueue.quit(false); }
public boolean isIdling(){ return mQueue.isIdling();}
} // class end.
Demo
Demo#1 发送消息
class SomeActivity
{
@Override
public void onCreate(...){
testMessage();
}
void testMessage(){
MyHandler handler = new MyHandler();
// or
handler = new Handler(/*this.getMainLooper(),*/ new Handler.Callback(){ /*...*/ });
// 发送Message
Message m1 = new Message();
m.setTarget(h);
m.sendToTarget();
// 发送Runnable(内部其实是发送了一个Message+Runnable)
handler.post(new Runnable(){
@Override
public void run(){
// 在主线程中运行
}
});
}
class MyHandler extends Handler{
@Override
public void handleMessage(Message msg){ /*...*/ }
}
Demo#2 自定义Looper线程
public static void testLooper()
{
Thread tLooper = new Thread(new Runnable(){
@Override
public void run() {
Looper.prepare();
MyHandler h = new MyHandler();
// 发送消息
h.sendEmptyMessage(0);
// 发送Runnable
h.post(new Runnable(){
@Override
public void run() {
Log.i("test","[in post runnable]threadid:"+Thread.currentThread().getId()); // tLooper线程ID
}});
Looper.loop(); // 一直循环。即使没有消息
}});
tLooper.start();
}
references
- Android 进阶14:源码解读 Android 消息机制( Message MessageQueue Handler Looper)
不要心急,一点一点的进步才是最靠谱的. 读完本文你将了解: 前言 Message 如何获取一个消息 Messageobtain 消息的回收利用 MessageQueue MessageQueue 的属 ...
- Android中消息系统模型和Handler Looper
http://www.cnblogs.com/bastard/archive/2012/06/08/2541944.html Android中消息系统模型和Handler Looper 作为Andro ...
- 深入理解Message, MessageQueue, Handler和Looper
做过Android的都知道Message, MessageQueue, Handler和Looper,但知道不代表你理解它们.有时觉得用得很顺手,但Android怎么实现又说不上来,总觉得似懂非懂.不 ...
- Android 基础 十一 Android的消息机制
Handler是Android消息机制的上层接口,这使得在开发应用过程中我们只需要和Handler交互即可.Handler的使用过程很简单,通过它可以轻松地将一个任务切换到Handler所在的线程中去 ...
- 【原创】源码角度分析Android的消息机制系列(五)——Looper的工作原理
ι 版权声明:本文为博主原创文章,未经博主允许不得转载. Looper在Android的消息机制中就是用来进行消息循环的.它会不停地循环,去MessageQueue中查看是否有新消息,如果有消息就立刻 ...
- 《Android开发艺术探索》读书笔记 (10) 第10章 Android的消息机制
第10章 Android的消息机制 10.1 Android消息机制概述 (1)Android的消息机制主要是指Handler的运行机制,其底层需要MessageQueue和Looper的支撑.Mes ...
- Android的消息机制
一.简介 ①.我们不能在子线程中去访问UI空控件,这是时候只能通过Handler将更新UI的操作放到主线程中去执行 ②.Handler的组成:messageQueue和Looper的支持 ③.Mess ...
- 【原创】源码角度分析Android的消息机制系列(一)——Android消息机制概述
ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 1.为什么需要Android的消息机制 因为Android系统不允许在子线程中去访问UI,即Android系统不允许在子线程中更新UI. 为什 ...
- Android的消息机制简单总结
参考文章: http://gityuan.com/2015/12/26/handler-message-framework/#next 参考资料: Android Framework的源码: Mess ...
随机推荐
- js之文档对象的设置(DOM)
1.对象文本: 对象.innerHTML; 对象.innerHTML=""; 对象.innerText; 对象.innerText=""; 2.对象属性: ...
- 运行iis出现:The server has encountered an error while loading an application ……的解决办法【转】
本人测试了下,第三种方法成功了. 然后经过网上搜索,3种解决方法: 第一种: MC检测到此管理单元发生一个错误.建议关闭并重新启动MMC 要变通解决此问题, 请按照下列步骤操作进入 WMI MMC 管 ...
- keil 的头文件 .
许多初学者使用网上下载的程序时都会遇到这样一个问题,就是头文件找不到.我想就这个问题说明一下./·首先,我们用到的KEIL有几种版本的,头文件也不同.有reg51.h和at89x51.h两种比较常见. ...
- wamp链接mysql数据库
一:链接到自带的数据库 1.打开mysql命令行 密码为空即回车2.输入use mysql 3.执行 update user set password=PASSWORD('123456') where ...
- 502 Server dropped connection
在本地电脑上开启了,全局VPN代理后,出现 502 报错. 502 Server dropped connection The following error occurred while tryin ...
- 分享MYSQL中的各种高可用技术(源自姜承尧大牛)
分享MYSQL中的各种高可用技术(源自姜承尧大牛) 图片和资料来源于MYSQL大牛姜承尧老师(MYSQL技术内幕作者) 姜承尧: 网易杭州研究院 技术经理 主导INNOSQL的开发 mysql高可用各 ...
- solr与.net系列课程(三)solr连接数据库
solr与.net系列课程(三)solr连接数据库 上一章直接讲述的配置文件把大部分人看的很迷惑,大家都想听的是solr到底是怎么用的,好,这一节我们就开始链接数据库,首先讲一下连接之前都要配置哪些 ...
- Visual Studio 2013 新功能 Memory Dump 分析器
本文为 Dennis Gao 原创技术文章,发表于博客园博客,未经作者本人允许禁止任何形式的转载. TechEd2013 发现新功能 12月5日和6日,在国家会议中心参加了微软的 TechEd2013 ...
- 单元测试 Mocking 类库需具备的特性
一个优秀的单元测试 Mocking 类库,需要具备如下几个特性: 易用性:有非常明确的 API ,易于使用并易于记忆. 健壮性:行为结果始终一致,并保持准确. 帮助性:当程序出错时,给出尽可能明确的原 ...
- [JS1] 如何嵌入
<html> <head> <title>在HTML文档中嵌入JavaScript代码是如何嵌入到HTML文档中的.</title> <scrip ...