我为什么写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 原理分析和使用(一)的更多相关文章

  1. Handler 原理分析和使用(二)

    在上篇 Handler 原理分析和使用(一)中,介绍了一个使用Handler的一个简单而又常见的例子,这里还有一个例子,当然和上一篇的例子截然不同,也是比较常见的,实例如下. import andro ...

  2. Handler 原理分析和使用之HandlerThread

    前面已经提到过Handler的原理以及Handler的三种用法.这里做一个非常简单的一个总结: Handler 是跨线程的Message处理.负责把Message推送到MessageQueue和处理. ...

  3. Handler系列之原理分析

    上一节我们讲解了Handler的基本使用方法,也是平时大家用到的最多的使用方式.那么本节让我们来学习一下Handler的工作原理吧!!! 我们知道Android中我们只能在ui线程(主线程)更新ui信 ...

  4. [转]Handler MessageQueue Looper消息循环原理分析

    Handler MessageQueue Looper消息循环原理分析   Handler概述 Handler在Android开发中非常重要,最常见的使用场景就是在子线程需要更新UI,用Handler ...

  5. Android中Input型输入设备驱动原理分析(一)

    转自:http://blog.csdn.net/eilianlau/article/details/6969361 话说Android中Event输入设备驱动原理分析还不如说Linux输入子系统呢,反 ...

  6. 使用AsyncTask异步更新UI界面及原理分析

    概述: AsyncTask是在Android SDK 1.5之后推出的一个方便编写后台线程与UI线程交互的辅助类.AsyncTask的内部实现是一个线程池,所有提交的异步任务都会在这个线程池中的工作线 ...

  7. 转载:solr MoreLikeThis的原理分析

    转载地址:http://blog.sina.com.cn/s/blog_5ddc071f0101muos.html 在solr中有两种方式实现MoreLikeThis:MoreLikeThisHand ...

  8. WebViewJavascriptBridge 原理分析

    WebViewJavascriptBridge 原理分析 网上好多都是在介绍 WebViewJavascriptBridge如何使用,这篇文章就来说说 WebViewJavascriptBridge ...

  9. Android中Input型输入设备驱动原理分析<一>

    话说Android中Event输入设备驱动原理分析还不如说Linux输入子系统呢,反正这个是没变的,在android的底层开发中对于Linux的基本驱动程序设计还是没变的,当然Android底层机制也 ...

随机推荐

  1. Android webview中cookie增加/修改

    最近项目需求中,需要满足往webview传递cookie,而且cookie需要增加修改: public class MainActivity extends Activity { private We ...

  2. [资源共享]C#+AE构建GIS桌面端应用系统框架-全代码

    转自:http://www.cnblogs.com/gispeng/archive/2008/10/06/1304534.html    [资源共享]C#+AE构建GIS桌面端应用系统框架-全代码 ( ...

  3. (转载)php获取form表单中name相同的表单项

    (转载)http://hi.baidu.com/ruhyxowwzhbqszq/item/5fd9c8b9b594db47ba0e12a9 比如下面的表单: /*form.php*/ <form ...

  4. Ext.MessageBox的用法

    1.Ext.MessageBox.alert()方法 有四个参数:alert( title , msg , function(){} ,this) 其中title,msg为必选参数,function为 ...

  5. Selenium WebDriver + Grid2 + RSpec之旅(五)---面向对象设计用例

    Selenium WebDriver + Grid2 + RSpec之旅(五) ----面向对象设计用例 前几节讲了怎么一步一步的从零开始到编写出一个简单的测试用例,这一节将要讲一下怎么让测试用例变得 ...

  6. Web---HTTP请求、重定向、转发和数据压缩

    HTTP常用的请求方式包括: GET-最为常见,但发送的数据量很小,发送的数据直接包含到url的后面. POST-可以包含大量数据,数据在请求正文中通过表单进行提交. HEAD,PUT,DELETE. ...

  7. java 解惑

    java对转义字符没有提供任何特殊处理.编译器在将程序解析成各种符号之前,先将 Unicode 转义字符转换成为它们所表示的字符[JLS 3.2]

  8. Yii PHP 框架分析(三)

    作者:wdy http://hi.baidu.com/delphiss/blog/item/357663d152c0aa85a1ec9c44.html Yii应用的入口脚本引用出了Yii类,Yii类的 ...

  9. Yii框架tips

    db组件 'schemaCachingDuration'=>3600, 为什么不起做用?需要开缓存 如何在页面下边显示sql的查询时间在log组件的routes中加入 array('class' ...

  10. HttpURLConnection的流式输出的缺陷和解决方法

    转自:http://www.mzone.cc/article/198.html 最近在用applet写文件上传控件的时候发现使用URLConnection来对服务器进行流式输出时的一些问题.我们通常要 ...