什么是Handler?

Android 的官方解释:

文档分节1:A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

这个解释给我们释放出以下几个信息:

一 Handler可以发送和处理两种类型的事物:

1,Message;

2,实现Runnable接口的对象

二,每个Handler实例都会跟创建Handler的Thread及这个Thread下的MessageQueue相关联。

文档分节2:There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

Handler主要有两个作用:

1,可以设定某个时间点去执行Message和Runnable对象

2,可以在不同的线程中去执行某项任务

文档分节3:Scheduling messages is accomplished with the post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long),sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) methods. The post versions allow you to enqueue Runnable objects to be called by the message queue when they are received; the sendMessage versions allow you to enqueue a Message object containing a bundle of data that will be processed by the Handler's handleMessage(Message) method (requiring that you implement a subclass of Handler).

Handler主要提供了两种方式来调度消息:

1,post方式:处理Runnable对象

2,sendmessage方式:在Message中放入Bundle data,这个消息会在Handler提供的handleMessage(Message)方法中去处理;

文档分节4:When posting or sending to a Handler, you can either allow the item to be processed as soon as the message queue is ready to do so, or specify a delay before it gets processed or absolute time for it to be processed. The latter two allow you to implement timeouts, ticks, and other timing-basedbehavior.

When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. Thegiven Runnable or Message will then be scheduled in the Handler's message queue and processed when appropriate.

1,我们可以让handler马上或者指定一个时间间隔发送消息;

2,当我们的应用开启时,UI线程会自动帮我们创建好MessageQueue,我们也可以在自己定义的Thread中创建。

这些信息我们现在只要稍微有个概念就好,一会进入Handler的深入分析之后,自然就会清楚,我们先来分析一下Handler的源码:

先来看看Handler主要的成员变量:

 final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;

我们先粗略地看一下,Handler这个类里面有MessageQueue,从名称中我们可以猜测,这个就应该是消息队列了,第二个成员变量:Looper,从名称来看,应该是个推动消息循环的角色,第三个成员变量:Callback,应该是个回调的接口,剩下的两个先暂时忽略,这三个比较重要的成员变量咱们先混个眼熟,打个招呼就好,现在我们来看一下这个类的构造方法,依次先从最简洁的构造方法看起:

    public Handler() {
this(null, false);
}
 public Handler(Callback callback) {
this(callback, false);
}
    public Handler(Looper looper) {
this(looper, null, false);
}
    public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
    public Handler(boolean async) {
this(null, async);
}
    public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
         //想必大家对下面这句话不陌生吧?细心的朋友们会经常在开发环境中看到这句黄色警告(该Handler可能会导致内存泄漏),后面的系列我们再给出解决这个警告的示例代码
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
} 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 = callback;
mAsynchronous = async;
}
     public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

我了个大去,稍微看去洋洋洒洒出现了7个构造方法,仔细看下,其实前面五种构造方法都调用到了最后两种构造方法中的一种,那么我们就着重来看一下这两个构造方法

public Handler(Callback callback, boolean async)的构造方法里面都干了些啥?
1,通过Looper的myLooper()方法,得到一个Looper对象,将之传给Handler的成员变量mLooper;
如果拿到的这个对象为null,就会抛出异常,从异常的信息来看,说是没有在创建Handler的时候去调用Looper的prepare()方法;
2,从得到的Looper对象拿出MessageQueue对象,传给Handler的成员变量mQueue;
3,将入参callback传给Handler的成员变量mCallback;
看到这里,朋友们可能会发现这个Looper看来是个重要角色啊,忍不住想去一窥究竟,先忍一忍,好戏总是在后头,先来看一下另外一个重要的构造方法
public Handler(Looper looper, Callback callback, boolean async)
其实这个方法做的就是上面那个构造方法的事情,只不过,mLooper,mQueue,mCallback都是在调用之前就已经创建好了,直接赋值而已 上面已经看了Handler这个类的成员变量,构造方法,那么,我们现在就来看一下这个类里面都给外部提供了哪些接口和方法
    public interface Callback {
public boolean handleMessage(Message msg);
}

原来Callback真的就是一个接口,提供了一个回调的方法 handleMessage(Message msg),这个方法的名字有点熟悉,是不是就是我们经常见到的那个在新创建一个Handler的时候,需要覆盖的方法呢?先带着疑问往下看,

 /**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}

看到这个类的注解,大家马上发现上面的疑问是自己多想了,两个方法虽然同名,但是返回类型不同啊,原来这个方法才是我们常见的那个需要覆盖的方法。

接着往下看,

在文章开头的文档分节3中,我们知道,Handler主要提供了两种方式来发送消息,一种是postXX方法,一种是sendMessageXX方法

果然,我们看到了大量的post和sendmessage方法

    public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
   public final boolean postAtTime(Runnable r, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
 public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
 public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
    public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
 public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
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);
}

细心的朋友们仔细一看,就能发现,其实不管是postXX还是sendMessageXX方法,最终调用的都是sendMessageAtTime这个方法,postXX方法只是不是多调了getPostMessage()方法而已 ,那么我们就来看一下这个方法做了些什么事情:

  private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
} private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}

首先获取一个Message,然后把Runnable对象传递给Message的callback变量,返回一个Message;

我们在文章开头中的Handler的官方文档中提到这么一句话

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue.
那么,这里的callback就是这个Runnable object了!
然后我们接着看sendMessageAtTime到底做了些啥?关键的代码就在最后一句:
return enqueueMessage(queue, msg, uptimeMillis);
从这个方法名中,我们可以知道它是让这个消息入列,我们来看一下它的具体实现:
 boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
} synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
msg.recycle();
return false;
} msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
} // We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}

分析第2行:msg.target,这个target指的是啥呢?我们这里先暂时不管,后来具体分析Message这个类的时候,我们会分析到

分析第10行:mQuitting,如果这个变量为true,那么就会抛出一个异常,在后续的分析IntentService的时候,我们会遇到这个异常,在这里先有个印象;

分析第33-45行:这里就是把消息按照消息的时间顺序进行排列而已,即把消息队列中的消息按照时间进行排序,并没有执行真正的入列操作,那什么时候执行真正的入列操作呢?后面分析Looper这个类的时候,我们会分析到


  /**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

从方法名称来看,这个方法是用来分发消息的,我们进入方法里面看一下,首先先判断Message的callback是否为空,这个callback到底是个啥?跟进去看一下

    /*package*/ Runnable callback;

是个Runnable,这里就是我们在getPostMessage这个方法中提到的把Runnable对象传递给Message的callback变量,也就是说,如果我们调用Hander的postXX的方法,那么就会把这个Runnable对象赋值给Message的callback变量,那么在上述代码的第五行中,当这个变量不为空的时候,就会调用handleCallback(msg):这个方法去处理消息,

private static void handleCallback(Message message) {
message.callback.run();
}

看到这里,大家就明白了,我们调用Handler 的postXX方法时,可以在这个Runnalble对象的run方法里去执行UI操作了。

接着分析上面代码的else分支,

 else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}

如果Handler的mCallback成员变量如果不为空,那么就调用这个接口的handleMessage方法,否则的话,就走handleMessage(msg)方法,

    /**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}

好了,这下终于到我们比较熟悉的Handler的处理消息方法上去了,这个方法就是我们在Handler里处理UI操作的方法了。

本篇只是单独地对Handler这个类分析它的内部实现及对外提供的接口及方法,并没有对与Handler紧密相关联的Looer类,MessageQueue类,Message进行分析,目的是为了清晰地对单个封闭的类进行感性的认识之后,再对与之关联的类相互之间的关联调用进行分析;

总结:

1,Handler可以处理 Message及Runnable对象;

2,Handler提供多种构造方法,从而得到与之紧密关联的Looper对象,MessageQueue对象等;

3,Handler提供了两种类型的发送消息的方法,post版本及sendMessage版本

4,Handler提供了重要的dispatchMessage方法,在这个方法中明确是把消息给Runnable对象的run方法还是给Handler子类的handlMessage方法执行UI操作。

 

 

Android Handler的使用示例:结合源码理解Android Handler机制(一)的更多相关文章

  1. 源码分析Android Handler是如何实现线程间通信的

    源码分析Android Handler是如何实现线程间通信的 Handler作为Android消息通信的基础,它的使用是每一个开发者都必须掌握的.开发者从一开始就被告知必须在主线程中进行UI操作.但H ...

  2. Android 进阶14:源码解读 Android 消息机制( Message MessageQueue Handler Looper)

    不要心急,一点一点的进步才是最靠谱的. 读完本文你将了解: 前言 Message 如何获取一个消息 Messageobtain 消息的回收利用 MessageQueue MessageQueue 的属 ...

  3. 50个Android开发人员必备UI效果源码[转载]

    50个Android开发人员必备UI效果源码[转载] http://blog.csdn.net/qq1059458376/article/details/8145497 Android 仿微信之主页面 ...

  4. [转载] 50个Android开发人员必备UI效果源码

    好东西,多学习! Android 仿微信之主页面实现篇Android 仿微信之界面导航篇Android 高仿QQ 好友分组列表Android 高仿QQ 界面滑动效果Android 高仿QQ 登陆界面A ...

  5. Android进阶:五、RxJava2源码解析 2

    上一篇文章Android进阶:四.RxJava2 源码解析 1里我们讲到Rxjava2 从创建一个事件到事件被观察的过程原理,这篇文章我们讲Rxjava2中链式调用的原理.本文不讲用法,仍然需要读者熟 ...

  6. Android 网络图片查看器与网页源码查看器

    在AndroidManifest.xml里面先添加访问网络的权限: <uses-permission android:name="android.permission.INTERNET ...

  7. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  8. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  9. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

随机推荐

  1. iOS后台定位时授权提示一闪而过的解决办法

    今天做后台定位时,授权提示"允许 XXX 在您并未使用该应用时范文您的位置吗?"总是一闪而过,点不到,或者压根就不弹出.后来找到了解决问题的方法,那就是:将CLLocationMa ...

  2. C#中调用python方法

    最近因为项目设计,有部分使用Python脚本,因此代码中需要调用python方法. 1.首先,在c#中调用python必须安装IronPython,在 http://ironpython.codepl ...

  3. vc6编译apache遇到的问题

    在apache的官网上下载了httpd-2.0.65-win32-src,打算了解一下其中的细节,以给目前做的一个项目提供抄袭性的参考.那是一个vc6的工程.用vc6一编译,apr那个工程报错,说什么 ...

  4. vim中输入tab符

    今天在写Makefile时各种出错.后来发现是all:的下一行,make前面必须是tab符,不能是空格. 但是vim中只要按tab就会自动转换成4个空格.平时编程需要,也不便把这个设置取消. 查了下, ...

  5. JavaWeb开发学习(一)-JavaWeb开发概述

    1.Web相关概念 Web程序也就是一般所说的网站,由服务器.客户端浏览器以及网络组成.Web程序的好处是使用简单,不需要安装.学习,有一台电脑.一根网线就可以使用.Web程序不是一般意义上的网站.网 ...

  6. (转载)JAVA线程池管理

    平时的开发中线程是个少不了的东西,比如tomcat里的servlet就是线程,没有线程我们如何提供多用户访问呢?不过很多刚开始接触线程的开发攻城师却在这个上面吃了不少苦头.怎么做一套简便的线程开发模式 ...

  7. dojo分析之declare接口

    欢迎转载opendevkit文章, 文章原始地址: http://www.opendevkit.com/?e=57 declare接口是dojo定义类系统的关键函数, 类系统就是抽象,封装,继承.do ...

  8. HTML5之Canvas时钟(网页效果--每日一更)

    今天,带来的是使用HTML5中Canvas标签实现的动态时钟效果. 话不多说,先看效果:亲,请点击这里 众所周知,Canvas标签是HTML5中的灵魂,HTML5 Canvas是屏幕上的一个由Java ...

  9. 【转】一个lucene的官网例子

    创建索引: import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import jav ...

  10. JavaScript中需要注意的几个问题

    JavaScript是一门弱语言,她使用起来不像C/C++那样有十分繁琐的内存管理.类型定义等,所以学习JavaScript的门槛相对来说也比较低.门槛低并不意味着这门语言很简单,我们在使用的时候会遇 ...