我为什么写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. oracle object_id和data_object_id的区别

    Oracle的数据字典表dba_objects包含了两个字段,object_id, data_object_id,官方文档上的解释是: object_id: Dictionary object num ...

  2. 大白书 209 remember the word

    F - Remember the Word Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Sub ...

  3. 打开URL操作

    /* * 打开 url 的两种方法 */ public class OpenUrl { public static void main(String[] args) { String url = &q ...

  4. Web---图片验证码生成教程详解-从简单到复杂-从本地到前后台

    首先,我们先来看本地如何生成图片验证码的,再来写输出到网页的验证码如何实现. 先来看最简单的-实现的功能是,将一个字符串变成图片写入到文件中 实现代码: package cn.hncu.img; im ...

  5. 移动平台WEB前端开发技巧

    1.首先我们来看看webkit内核中的一些私有的meta标签,这些meta标签在开发webapp时起到非常重要的作用 <meta content="width=device-width ...

  6. poj 1039 Pipe(几何基础)

    Pipe Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 9932   Accepted: 3045 Description ...

  7. Bzoj 2243: [SDOI2011]染色 树链剖分,LCT,动态树

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 5020  Solved: 1872[Submit][Status ...

  8. chgrp、chown、chmod命令

    讲解内容: 权限代号的含义如下: r:读写目录或文件的权限    read w :写入目录或文件的权限  write x :执行目录或文件的权限 -:删除目录或文件的权限 s:特殊权限,更改目录或文件 ...

  9. Ubuntu下Django初体验(一)——开发环境搭建

    一.开发 环境搭建 1. linux下一般有自带的python,如果对版本不满意,可以再自行安装. 2. 安装ipython(推荐) sudo apt-get install ipython sudo ...

  10. jvm参数设置大全

    转自:http://blog.csdn.net/kthq/article/details/8618052 参数说明 -Xmx3550m:设置JVM最大堆内存为3550M. -Xms3550m:设置JV ...