Handler和Message以及Looper之间的三角关系
说到Handler想必大家都经常用到,在非UI线程更新UI那可是利器,用起来也非常容易上手
从使用上来说,我们只需要关注sendMessage和handleMessage即可
所以我们先从Handler和Message来说起,先看一小段代码
public static final int UPDATE_TEXT_VIEW = 0;
public TextView mResultTextView = null; // new 一个 Handler 对象, 以内部类的方式重写 handleMessage 这个函数
public Handler mMyHandler = new Handler() {
// ③ 处理消息
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case UPDATE_TEXT_VIEW:
/* 这里可更新 ui */
mResultTextView.setText(String.valueOf(msg.arg1));
break; default:
/* do nothing */
break;
}
};
}; // ==================================================================
// 函数名: calc
// 日期: 2015-08-30
// 功能: 计算入参并显示在 UI 上,然后后续以每秒 +1 在 UI 上更新
// 输入参数: int a
// int b
// 返回值: 无
// 修改记录:千里草新增函数
// ==================================================================
public void calc(int a, int b) {
int c = a + b;
final Message msg = new Message();
msg.what = UPDATE_TEXT_VIEW;
msg.arg1 = c;
// ① 发送消息
mMyHandler.sendMessage(msg);
// 这里更新 UI 线程的TextView OK
// mResultTextView.setText(String.valueOf(msg.arg1)); // 启动一个新的线程来每秒刷新 TextView
new Thread() {
public void run() {
while (true) {
msg.arg1++;
// ② 发送消息
mMyHandler.sendMessage(msg); // mResultTextView.setText(String.valueOf(msg.arg1)); 这里会报错
try {
sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} };
}.start();
}
代码中有①,②两处发送消息,. 分别是从UI线程和从子线程发送的消息, 在③处两个消息都可以被准确的接收到.
在接受消息函数handleMessage 里我们可以做任何想做的事情,包括更新UI…
从使用来说,大家根据这个例子,想必已经可以简单使用Handler来处理多线程之间的交互了…
此处需要注意的是在使用Handler时,必须需要重写handleMessage 才能达到我们想达到的目的...(从逻辑上来说,没有接受消息的地方,发送的消息有何意义呢,是吧….从原理上..我们接下来会谈到……)
谈到Handler,我们还可以用它执行一个Runnable多线程…例如下面的代码..
mMyHandler.post(new Runnable() { @Override
public void run() {
// 这里需要注意的是,calc 是在子线程里被调用,所以calc 就无法操作 UI 了.
calc(1, 2);
}
});
Handler可真神奇,既可以发消息也可以执行某个任务线程.
为了更深层次的了解它,只能去看看它的源码了.let’s go!
先看看是如何post一个Runnable线程的
public final boolean post(Runnable r)
{
//这里实际上是发送了一个Message
return sendMessageDelayed(getPostMessage(r), 0);
} //组装一个只带Runnable的Message
private static 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);
}
哎哟喂,这Handler小丫头片子,还假装两幅面孔呢, 经查看源码,发现它实际上是发送了一个Message消息,然而这个消息仅仅携带了一个Runnable对象.
现在来看只需要分析Handler和Message了..我们来看看最终Handler是如何发送消息的..我们继续看源码,非得把他们扒个精光不可
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
//消息队列,这里存储Handler发送的消息
MessageQueue queue = mQueue;
if (queue != null) {
//Message与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;
}
从消息最终被发出的函数来看,这里只做了两个事情:
1.Message与Handler绑定
2.将Message放进消息队列里
消息进队列之后,Handler发送消息的任务算是完美的完成了,接下来我们该介绍介绍Looper了,如果没有Looper,Handler和Message之间的爱情可以说是不完整的.
我们先来看看一个Looper是如何使用的吧
class LooperThread extends Thread {
public Handler mHandler; public void run() {
// 为当前线程准备一个 Looper
Looper.prepare(); mHandler = new Handler() {
public void handleMessage(Message msg) {
// 处理消息
}
};
// Looper 开始工作
Looper.loop();
}
}
//继续看源码,prepare执行之后sThreadLocal绑定一个Looper对象
//sThreadLocal 能且只能绑定一个Looper对象
public static void prepare() {
prepare(true);
} 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));
} /**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
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; // Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity(); //无条件的for循环,来遍历Messagequeue
for (;;) {
//获取下一条消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
} // This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
} //Message绑定的Handler来开始处理消息.. 下面贴上dispatchMessage,函数,大家继续往下看
msg.target.dispatchMessage(msg); if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
} // Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
} msg.recycle();
}
}
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
public boolean handleMessage(Message msg);
} /**
* Subclasses must implement this to receive messages.
* 子类必须完成handleMessage这个函数来接收消息
*/
public void handleMessage(Message msg) {
} /**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
//收到消息后,先处理Runnable 对象callback.
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果这里有回调接口,那么就直接调用该接口,不再继续调用Handler的handleMessage函数来处理消息
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
从上述加了中文注释的代码中能看出,Handler发送的Message通过Looper分发给了各自的Handler.
所以上面说的在使用Handler时,必须需要重写handleMessage 才能达到我们想达到的目的...也不完全正确,
我们也可以实现一个CallBack接口来处理消息,用Google的原话是
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
再贴最后一段代码来彻彻底底说明Handler Looper之间的关系, 那就是Handler的 构造函数
/**
* Default constructor associates this handler with the queue for the
* current thread.
*
* If there isn't one, this handler won't be able to receive messages.
*/
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());
}
}
//绑定当前线程的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 = null;
}
一句话总结来说,Handler初始化时绑定了线程的MessageQueue,当前线程的Looper来依次分发MessageQueue里的消息.
MessageQueue的消息会根据Message的target绑定的Handler被Looper分发到各自的Handler里去处理.
例如Activity里有两个HandlerA和HandlerB, HandlerA发送的消息绝对不可能被HandlerB处理..
先暂时写到这里吧,后续补上流程图和类图
上述只是个人的很片面的理解,希望大家补充和指出不足的地方…
2015年9月4日 04:10:05 千里草
Handler和Message以及Looper之间的三角关系的更多相关文章
- Android的消息处理机制,handler,message,looper(一)
当应用程序启动时,Android首先会开启一个主线程(也就是UI线程),主线程为管理界面中的UI控件.在程序开发时,对于比较耗时的操作,通常会为其开辟一个单独的线程来执行,以尽可能减少用户的等待时间. ...
- Android 消息机制 (Handler、Message、Looper)
综合:http://blog.csdn.net/dadoneo/article/details/7667726 与 http://android.tgbus.com/Android/androidne ...
- Android面试题 请解释下单线程模型中Message、Handler、MessageQueue、Looper之间的关系
简单的说,Handler获取当前线程中的looper对象,looper用来存放从MessageQueue中取出的Message,再由Handler进行Message分发和处理,按照先进先出执行. Me ...
- Android开发学习之路--异步消息Handler,Message,Looper和AsyncTask之初体验
在简易音乐播放器中,用了Handler,也没有过多地去研究学习,这里再学习下android下的异步消息处理机制.这里用了Handler主要是在线程中不能更新UI,而需要通过Handler才可以.关于异 ...
- Handler,Thread,Looper之间关系小结
http://blog.csdn.net/sunxingzhesunjinbiao/article/details/6794840 (1) Looper类别用来为一个线程开启一个消息循环.默认情况下A ...
- javascript中prototype、constructor以及__proto__之间的三角关系
三者暧昧关系简单整理 在javascript中,prototype.constructor以及__proto__之间有着“著名”的剪不断理还乱的三角关系,楼主就着自己对它们的浅显认识,来粗略地理理以备 ...
- 请解释下在单线程模型中Message、Handler、MessageQueue、Looper之间的关系
对于面试,每个职场人士都经历过,面试官更看中你对于技术的理解是否透彻,需要知其所以然,而实际工作中看中的工作效率,都是在使用API的角度来完成任务,当在一家公司呆久了有跳槽的想法时,个人的亲身经历就是 ...
- Looper Handler MessageQueue Message 探究
Android消息处理的大致的原理如下: 1.有一个消息队列,可以往队列中添加消息 2.有一个消息循环,可以从消息队列中取出消息 Android系统中这些工作主要由Looper和Handler两个类来 ...
- 深入了解Looper、Handler、Message之间关系
深入了解Looper.Handler.Message之间关系 前言及简介 上个星期我们整个项目组趁着小假期,驱车去了江门市的台山猛虎峡玩了两个多钟左右极限勇士全程漂流,感觉真得不错,夏天就应该多多玩水 ...
随机推荐
- java中对List中对象排序实现
package com.test; import java.util.ArrayList; import java.util.Collections; import java.util.Compara ...
- C++调用C#之C# COM控件
C#做界面真的是比C++方便多了,所以尝试了一下,使用C++做核心功能(例如绘图),然后用C#来做节目(例如对话框),考虑到以后可能不能使用.net,使用DLL做一个隔离层,隔离C++和C#,方便以后 ...
- linux通用邻居基础架构
1.为每一个协议提供一个缓存来存放L3到L2的转换结果. 2.提供在缓存中添加.删除.改变和查找一个特定映射项的函数.查找函数必须要快,因为它会影响整个系统的性能. 3.为每一个协议缓存的数据项提供一 ...
- Hrbustoj 2266 Legendary Weights(辗转相除求最大公约数)
题意:这个题目的意思是给出一些砝码,问我们能不能根据这些砝码称量出任意重量的物品,最大公约数并不难求,难的在于如何建立这个模型. 思路:根据数论的基础知识,两个数a,b的最大公约数是a*x + b*y ...
- Servlet与JSP的异同
1.什么是Servlet A Java servlet is a Java programming language program that extends the capabilities of ...
- CNS数据库网站只用mysql自带的fulltext index功能就可实现了。
1)编辑脚本script.sql如下 ALTER TABLE `table_name` ADD FULLTEXT (`column_name`) 2)在mysql console下输入命令 SOURC ...
- 我的android学习脚步----------- 的第一个应用
刚刚开始学android开发,以前都是在别人调好的应用中修改JNI,现在需要自己一步步走 开发环境:Eclipse+ADT 配置不多讲了,引自:http://www.cnblogs.com/allen ...
- 小红的难题<递推>
题意:五个数:N,x,y,A,B;N是台阶总数,x,y是每步可以走x或者y步,但是一定要走到A,B台阶上. 思路:学长给的题解,递推,稍微优化一点. >重点在递推 #include<cst ...
- iptables-过滤61开头的ip数据包
iptables -F root@android:/system/bin # iptables -L -n iptables -L -n Chain INPUT (policy ACCEPT) tar ...
- Python大神成长之路: 第一次学习记录
一.Python发展史 二.Python2 or 3 博主选择了Python3. 从官网下载Python www.python.org Windows安装python3.5.python2.7.安装 ...