Handler 原理分析和使用(一)
我为什么写Handler,原因主要还在于它在整个 Android 应用层面非常之关键,他是线程间相互通信的主要手段。最为常用的是其他线程通过Handler向主线程发送消息,更新主线程UI。
下面是一个最简单的例子。
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView; public class MainActivity extends AppCompatActivity { private TextView myTextView;
private Handler myHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
//UI线程接收到消息 int arg1 = msg.arg1;
switch (arg1){
case 0:
if(msg.arg2 == 0){
//更新UI
myTextView.setText((String)msg.obj);
}
break;
default:
break;
}
super.handleMessage(msg); }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myTextView = (TextView)this.findViewById(R.id.text_view);
//起独立线程
new Thread(){
public void run(){
String text = "from handler";
Message message = new Message();
message.arg1 = 0;
message.arg2 = 0;
message.obj = text;
//通过Handler给UI发消息 myHandler.sendMessage(message);
}
}.start();
}
}
上面的例子看似好简单了。但是支持这样消息从一个线程传到另一个线程,不仅仅需要Handler这样一个类的支持,还需要其他类的支持,分别是 Looper, Message, MessageQueue。
消息的流转的架构:
- Handler 负责发送消息和处理消息
- Message 是消息的实体。
- MessageQueue 消息队列。
- Looper 负责消息队列的循环,包括两件事:第一创建和控制 MessageQueue;第二轮询MessageQueue读取Message信息派发给Handler
消息的流转的过程:
首先,在Android里面每一个线程都有自己的一个Looper。而每个Looper都有一个MessageQueue。
Looper对象不需要开发人员去初始化,在每个线程里面他是存在的。源码中初始化如下:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
可见消息队列也是在此创建的。但是每个线程需要绑定自己的Looper,调用的方法是Looper.prepare(),源码实现如下
public static final void prepare() {
//此处说明prepare只能执行一次,再一次会抛异常
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//绑定Looper
sThreadLocal.set(new Looper(true));
}
其次,Handler对象可以跨线程,它在次线程中将Message推入MessageQueue中。
在Handler初始化时,就已经和自己所在的线程的MessageQueue绑定,源码如下
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
..............
//获取Looper对象
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//绑定MessageQueue对象
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
绑定之后就可以向MessageQueue里面推入Message,源码如下:
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
由上可以看出各种推Message的方法最后都归结到 enqueueMessage(...)方法中,该方法实现源码如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//此处,Handler对象被Message对象标记起来,
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//压入MessageQueue里面。
return queue.enqueueMessage(msg, uptimeMillis);
}
再次,Looper发现MessageQueue有Message,于是获取该Message相应的Handler,并将Messager给Handler处理。
Looper又是如何发现MessageQueue里面的Message,并且分配给指定的Handler?答案是通过Looper.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.");
}
//获取到Looper对象的MessageQueued对象。
final MessageQueue queue = me.mQueue; .............
//开始无限循环
for (;;) {
//从MessageQueue里面读取Message,如果消息暂时不被读取会被阻塞。
Message msg = queue.next();
if (msg == null) {
// No message indicates that the message queue is quitting.
//消息为空退出。
return;
} ............
//此处给Message的target(也就是对应的Handler)指派消息。 msg.target.dispatchMessage(msg); ............
//消息被回收
msg.recycle();
}
}
最后,Handler处理该Messager
接上面的源码可知,最后Message又被它的发送者Handler进行处理,调用的方法是dispatchMessage(msg),该方法源码实现如下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//该方法会被重写,从而实现自定义的UI改动
handleMessage(msg);
}
}
可以看出,最后调用到了handlerMessage(msg)方法,在通常的实践中,这个方法被重写,从而实现自定义的逻辑。
以上过程中需要注意。
Message对象本身存在于一个消息池中。如果消息池中有消息,建议不要使用new的方式产生对象应该复用该对象
Message message = myHandler.obtainMessage();
message.arg1 = 0;
myHandler.sendMessage(message);
今天就到这吧。这只是一部分,明天再说另一部分。同样的作为进程间通信的开源组件EventBus也将被分析。
Handler 原理分析和使用(一)的更多相关文章
- Handler 原理分析和使用(二)
在上篇 Handler 原理分析和使用(一)中,介绍了一个使用Handler的一个简单而又常见的例子,这里还有一个例子,当然和上一篇的例子截然不同,也是比较常见的,实例如下. import andro ...
- Handler 原理分析和使用之HandlerThread
前面已经提到过Handler的原理以及Handler的三种用法.这里做一个非常简单的一个总结: Handler 是跨线程的Message处理.负责把Message推送到MessageQueue和处理. ...
- Handler系列之原理分析
上一节我们讲解了Handler的基本使用方法,也是平时大家用到的最多的使用方式.那么本节让我们来学习一下Handler的工作原理吧!!! 我们知道Android中我们只能在ui线程(主线程)更新ui信 ...
- [转]Handler MessageQueue Looper消息循环原理分析
Handler MessageQueue Looper消息循环原理分析 Handler概述 Handler在Android开发中非常重要,最常见的使用场景就是在子线程需要更新UI,用Handler ...
- Android中Input型输入设备驱动原理分析(一)
转自:http://blog.csdn.net/eilianlau/article/details/6969361 话说Android中Event输入设备驱动原理分析还不如说Linux输入子系统呢,反 ...
- 使用AsyncTask异步更新UI界面及原理分析
概述: AsyncTask是在Android SDK 1.5之后推出的一个方便编写后台线程与UI线程交互的辅助类.AsyncTask的内部实现是一个线程池,所有提交的异步任务都会在这个线程池中的工作线 ...
- 转载:solr MoreLikeThis的原理分析
转载地址:http://blog.sina.com.cn/s/blog_5ddc071f0101muos.html 在solr中有两种方式实现MoreLikeThis:MoreLikeThisHand ...
- WebViewJavascriptBridge 原理分析
WebViewJavascriptBridge 原理分析 网上好多都是在介绍 WebViewJavascriptBridge如何使用,这篇文章就来说说 WebViewJavascriptBridge ...
- Android中Input型输入设备驱动原理分析<一>
话说Android中Event输入设备驱动原理分析还不如说Linux输入子系统呢,反正这个是没变的,在android的底层开发中对于Linux的基本驱动程序设计还是没变的,当然Android底层机制也 ...
随机推荐
- ruby eclipse调试
rubyinstaller 1.9.3eclipse Keplermarketplace ruby dltk 5.0ruby devkit(Ruby 1.8.7 and 1.9.3) DevKit-t ...
- (转载)Javascript操作表单之间的数据传递
(转载)http://www.aspxhome.com/javascript/skills/200710/214825.htm 今天有朋友问我关于用JAVASCRIPT来进行页面各表单之间的数据传递的 ...
- Color the ball HDOJ--1556
Color the ball Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)To ...
- Android环境rm命令
How can I execute all the possible unix(shell) commands in android programmatically? Android can't e ...
- tcp/ip体系-转载
如果还想在测试这条路上继续走下去的话,那么下面这些东西就是我们必须去掌握的,至少你还不想止步于简单的黑盒测试--其实,一直想去接触Linux下的应用测试,这样能学到东西会很多,而且会非常的受用.之前听 ...
- sql 将Null 值转化成空字符串
当Null + 任何字符串时,都等于Null. 因些用函数IsNull(字段名,''),如果字段名中的值是Null时,那么这个字段名的值是''. 例如::select code + IsNull('- ...
- SLua 中使用 Lua 5.3 的编译工程
2016-03-05 更新: 之前编译的库,在 Android 下 Lua_Number 和 Lua_Integer 被编译为了32位,导致从 C# 到 Lua 过程中有64位到32位整型转换会出现溢 ...
- hdoj 4006 The kth great number【优先队列】
The kth great number Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65768/65768 K (Java/Oth ...
- String类、 StringBuffer、基本数据类型对象包装类
一.概述 Sting s1 = "abc"; //s1是一个类类型变量,"abc"是一个对象. String s2 = new String(" ...
- Mina学习之IoFilter
IoFilter 是MINA中的一个核心结构,扮演了非常重要的角色.IoFilter在IoService和IoHandler过滤了所有的I/O 事件和请求.如果你有做个web项目的经验,则很类似于we ...