我为什么写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. PHP 'ext/soap/php_xml.c'不完整修复多个任意文件泄露漏洞

    漏洞版本: PHP 5.4.1 PHP 5.3.13 PHP 5.3.12 PHP 5.3.11 PHP 5.3.10 PHP 5.3.1 PHP 5.3 漏洞描述: BUGTRAQ ID: 6237 ...

  2. StorSimple 简介

     2014年 10月 28日,星期二 PRACHEETI NAGARKAR DESAI 混合云存储业务资深项目经理 在此我很荣幸地宣布StorSimple解决方案已经在中国正式上市.该方案为IT部 ...

  3. Remarks on a preprint

    Page 2 Line 1, "reads" should be "read". Page 2 Line 5, "are initial veloci ...

  4. ARM学习笔记8——通用寄存器和存储器内容交换指令和软中断指令

    交换指令将一个存储单元内容与制定的寄存器内容相交换,交换指令为进程间同步提供了一种方便的解决途径.该指令产生一堆原子Load/Store操作,该操作发生在一个连续的总线操作中,在操作期间阻止其他任何指 ...

  5. [JLOI2013]卡牌游戏

    [题目描述 Description] N个人坐成一圈玩游戏.一开始我们把所有玩家按顺时针从1到N编号.首先第一回合是玩家1作为庄家.每个回合庄家都会随机(即按相等的概率)从卡牌堆里选择一张卡片,假设卡 ...

  6. POJ 3074 Sudoku (Dacing Links)

    推荐一个写数独很好的博客:http://www.cnblogs.com/grenet/p/3163550.html 主要是把九宫格里的元素换到矩阵里面再求解dancing links 网上找的一模版 ...

  7. JavaScript高级程序设计51.pdf

    (续上篇) 模拟鼠标事件 var btn=document.getElementById("myBtn"); //创建事件对象 var event=document.createE ...

  8. ubuntu遇到包依赖问题出错的解决方法

    更新时遇到了libc6包依赖错误,甚至“sudo apt-get -f install“也会报错, 这时候可以使用下列命令删除包后重新安装dpkg -r --force-all 包名称 然后再sudo ...

  9. kafka consumer 分区reblance算法

    转载请注明原创地址 http://www.cnblogs.com/dongxiao-yang/p/6238029.html 最近需要详细研究下kafka reblance过程中分区计算的算法细节,网上 ...

  10. struts2 表单处理

    在这篇教程里我们将探究如何处理表单提交.本文例子介绍: javabean存储表单数据 在action中重写validate方法进行简单的校验 创建一个struts2表单并和javabean匹配 jav ...