1、Handler的由来

当程序第一次启动的时候,Android会同时启动一条主线程( Main Thread)来负责处理与UI相关的事件,我们叫做UI线程。

  Android的UI操作并不是线程安全的(出于性能优化考虑),意味着如果多个线程并发操作UI线程,可能导致线程安全问题。

  为了解决Android应用多线程问题—Android平台只允许UI线程修改Activity里的UI组建,就会导致新启动的线程无法改变界面组建的属性值。

  简单的说:当主线程队列处理一个消息超过5秒,android 就会抛出一个 ANR(无响应)的异常,所以,我们需要把一些要处理比较长的消息,放在一个单独线程里面处理,把处理以后的结果,返回给主线程运行,就需要用的Handler来进行线程建的通信。

2、Handler的作用

2.1 让线程延时执行

主要用到的两个方法:

  • final boolean postAtTime(Runnable r, long uptimeMillis)

  • final boolean postDelayed(Runnable r, long delayMillis)

2.2 让任务在其他线程中执行并返回结果

分为两个步骤:

  • 在新启动的线程中发送消息

      使用Handler对象的sendMessage()方法或者SendEmptyMessage()方法发送消息。

  • 在主线程中获取处理消息

      重写Handler类中处理消息的方法(void handleMessage(Message msg)),当新启动的线程发送消息时,消息发送到与之关联的MessageQueue。而Hanlder不断地从MessageQueue中获取并处理消息。

3、Handler更新UI线程一般使用

  • 首先要进行Handler 申明,复写handleMessage方法( 放在主线程中)
private Handler handler = new Handler() {

        @Override
public void handleMessage(Message msg) {
// TODO 接收消息并且去更新UI线程上的控件内容
if (msg.what == UPDATE) {
// 更新界面上的textview
tv.setText(String.valueOf(msg.obj));
}
super.handleMessage(msg);
}
};
  • 子线程发送Message给ui线程表示自己任务已经执行完成,主线程可以做相应的操作了。
new Thread() {
@Override
public void run() {
// TODO 子线程中通过handler发送消息给handler接收,由handler去更新TextView的值
try {
//do something Message msg = new Message();
msg.what = UPDATE;
msg.obj = "更新后的值" ;
handler.sendMessage(msg);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();

 4、Handler原理分析

4.1  Handler的构造函数

① public Handler()
② public Handler(Callbackcallback)
③ public Handler(Looperlooper)
④ public Handler(Looperlooper, Callbackcallback)  
  • 第①个和第②个构造函数都没有传递Looper,这两个构造函数都将通过调用Looper.myLooper()获取当前线程绑定的Looper对象,然后将该Looper对象保存到名为mLooper的成员字段中。  
      下面来看①②个函数源码:
113    public Handler() {
114 this(null, false);
115 } 127 public Handler(Callback callback) {
128 this(callback, false);
129 } //他们会调用Handler的内部构造方法 188 public Handler(Callback callback, boolean async) {
189 if (FIND_POTENTIAL_LEAKS) {
190 final Class<? extends Handler> klass = getClass();
191 if ((klass.isAnonymousClass() ||klass.isMemberClass()
|| klass.isLocalClass()) &&
192 (klass.getModifiers() & Modifier.STATIC) == 0) {
193 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
194 klass.getCanonicalName());
195 }
196 }
197/************************************
198 mLooper = Looper.myLooper();
199 if (mLooper == null) {
200 throw new RuntimeException(
201 "Can't create handler inside thread that has not called Looper.prepare()");
202 }
203 mQueue = mLooper.mQueue;
204 mCallback = callback;
205 mAsynchronous = async;
206 }

  我们看到暗红色的重点部分:

  通过Looper.myLooper()获取了当前线程保存的Looper实例,又通过这个Looper实例获取了其中保存的MessageQueue(消息队列)。每个Handler 对应一个Looper对象,产生一个MessageQueue

  • 第③个和第④个构造函数传递了Looper对象,这两个构造函数会将该Looper保存到名为mLooper的成员字段中。
      下面来看③④个函数源码:
136    public Handler(Looper looper) {
137 this(looper, null, false);
138 }  147 public Handler(Looper looper, Callback callback) {
148 this(looper, callback, false);
149 }
//他们会调用Handler的内部构造方法 227 public Handler(Looper looper, Callback callback, boolean async) {
228 mLooper = looper;
229 mQueue = looper.mQueue;
230 mCallback = callback;
231 mAsynchronous = async;
232 }
  • 第②个和第④个构造函数还传递了Callback对象,Callback是Handler中的内部接口,需要实现其内部的handleMessage方法,Callback代码如下:
80     public interface Callback {
81 public boolean More ...handleMessage(Message msg);
82 }

Handler.Callback是用来处理Message的一种手段,如果没有传递该参数,那么就应该重写Handler的handleMessage方法,也就是说为了使得Handler能够处理Message,我们有两种办法:
  
 1. 向Hanlder的构造函数传入一个Handler.Callback对象,并实现Handler.Callback的handleMessage方法  
  
 2. 无需向Hanlder的构造函数传入Handler.Callback对象,但是需要重写Handler本身的handleMessage方法

也就是说无论哪种方式,我们都得通过某种方式实现handleMessage方法,这点与Java中对Thread的设计有异曲同工之处。

4.2 Handle发送消息的几个方法源码

   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);
}

我们可以看出他们最后都调用了sendMessageAtTime(),然后返回了enqueueMessage方法,下面看一下此方法源码:

626    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
      //把当前的handler作为msg的target属性
627 msg.target = this;
628 if (mAsynchronous) {
629 msg.setAsynchronous(true);
630 }
631 return queue.enqueueMessage(msg, uptimeMillis);
632 }

在该方法中有两件事需要注意:

  1. msg.target = this

      该代码将Message的target绑定为当前的Handler

  2. queue.enqueueMessage
      
      变量queue表示的是Handler所绑定的消息队列MessageQueue,通过调用queue.enqueueMessage(msg, uptimeMillis)我们将Message放入到消息队列中。

过下图可以看到完整的方法调用顺序:

5. 如何在子线程中使用Handler

  Handler本质是从当前的线程中获取到Looper来监听和操作MessageQueue,当其他线程执行完成后回调当前线程。

  子线程需要先prepare()才能获取到Looper的,是因为在子线程只是一个普通的线程,其ThreadLoacl中没有设置过Looper,所以会抛出异常,而在Looper的prepare()方法中sThreadLocal.set(new Looper())是设置了Looper的。

5.1 实例代码

 定义一个类实现Runnable接口或继承Thread类(一般不继承)。

    class Rub implements Runnable {  

        public Handler myHandler;
// 实现Runnable接口的线程体
@Override
public void run() { /*①、调用Looper的prepare()方法为当前线程创建Looper对象并,
创建Looper对象时,它的构造器会自动的创建相对应的MessageQueue*/
Looper.prepare(); /*.②、创建Handler子类的实例,重写HandleMessage()方法,该方法处理除当前线程以外线程的消息*/
myHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
String ms = "";
if (msg.what == 0x777) { }
} };
//③、调用Looper的loop()方法来启动Looper让消息队列转动起来
Looper.loop();
}
}

注意分成三步: 

1.调用Looper的prepare()方法为当前线程创建Looper对象,创建Looper对象时,它的构造器会创建与之配套的MessageQueue。  

2.有了Looper之后,创建Handler子类实例,重写HanderMessage()方法,该方法负责处理来自于其他线程的消息。  

3.调用Looper的looper()方法启动Looper。

  然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在你创建Handler实例的线程中运行。

6、总结

Handler
      发送消息,它能把消息发送给Looper管理的MessageQueue。
      处理消息,并负责处理Looper分给它的消息。
Message
      Handler接收和处理的消息对象。
Looper
      每个线程只有一个Looper,它负责管理对应的MessageQueue,会不断地从MessageQueue取出消息,并将消息分给对应的Hanlder处理。  
      
      主线程中,系统已经初始化了一个Looper对象,因此可以直接创建Handler即可,就可以通过Handler来发送消息、处理消息。 程序自己启动的子线程,程序必须自己创建一个Looper对象,并启动它,调用Looper.prepare()方法。

prapare()方法:保证每个线程最多只有一个Looper对象。  

looper()方法:启动Looper,使用一个死循环不断取出MessageQueue中的消息,并将取出的消息分给对应的Handler进行处理。  

MessageQueue:由Looper负责管理,它采用先进先出的方式来管理Message。 

  Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。 
  
  Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。

Android 异步消息处理机制的更多相关文章

  1. Android异步消息处理机制(多线程)

    当我们需要执行一些耗时操作,比如说发起一条网络请求时,考虑到网速等其他原因,服务器未必会立刻响应我们的请求,如果不将这类操作放在子线程里去执行,就会导致主线程被阻塞住,从而影响用户对软件的正常使用. ...

  2. 【转载】Android异步消息处理机制详解及源码分析

    PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbob ...

  3. Android异步消息处理机制

    安卓子线程无法直接更改UI,所以需要异步消息处理机制来解决 <?xml version="1.0" encoding="utf-8"?><Li ...

  4. Android 异步消息处理机制解析

    Android 中的异步消息处理主要由四个部分组成,Message.Handler.MessageQueue.Looper.下面将会对这四个部分进行一下简要的介绍. 1. Message: Messa ...

  5. 【转】Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Andr ...

  6. Android 异步消息处理机制 让你在深入了解 Looper、Handler、Message之间的关系

    转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 非常多人面试肯定都被问到过,请问And ...

  7. Android 异步消息处理机制终结篇 :深入理解 Looper、Handler、Message、MessageQueue四者关系

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.概述 我们知道更新UI操作我们需要在UI线程中操作,如果在子线程中更新UI会发生异常可能导致崩溃,但是在UI线程中进行耗时操作又会导致ANR,这 ...

  8. Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系

    转自:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Android中的 ...

  9. Android异步消息处理机制完全解析,带你从源码的角度彻底理解(转)

    开始进入正题,我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃.相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟于心,即创建一 ...

  10. 【转】Android异步消息处理机制完全解析,带你从源码的角度彻底理解

    原文网址:http://blog.csdn.net/guolin_blog/article/details/9991569 转载请注明出处:http://blog.csdn.net/guolin_bl ...

随机推荐

  1. aliyun阿里云Maven仓库地址

    <mirror> <id>nexus-aliyun</id> <mirrorOf>*</mirrorOf> <name>Nexu ...

  2. C# socket通信

    最近在研究socket,今天看到很好的一篇关于socket通信的文章,故收藏了,慢慢琢磨. 我们在讲解Socket编程前,先看几个和Socket编程紧密相关的概念: 1.TCP/IP层次模型 当然这里 ...

  3. HttpEntity转换Inputstream(红色)加XmlPull解析

    package com.bawei.xml; import java.io.IOException; import java.io.InputStream; import java.util.Arra ...

  4. iOS多线程同步锁

    在iOS中有几种方法来解决多线程访问同一个内存地址的互斥同步问题: 方法一,@synchronized(id anObject),(最简单的方法)会自动对参数对象加锁,保证临界区内的代码线程安全 @s ...

  5. MySQL MMM 双主在Failover时挂起

    两台MySQL服务器,版本Percona 5.5.35,配置双主,装在Centos 6.4下 Master1  db1,172.21.120.160(真实IP),172.21.120.173(Writ ...

  6. HTML入门教程

    什么是 HTML?     HTML(Hyper Text Markup Language)超文本标记语言,是用来描述网页的一种语言,不是一种编程语言,而是一种标记语言 (markup languag ...

  7. [转] How to debug a ARM Cortex-M hard fault exception

    how to debug a ARM Cortex-M hard fault exception

  8. [已解决] java 增加 ALPN支持

    添加javaVM参数: -Xbootclasspath/p:lib/alpn-boot-8.1.10.v20161026.jar 文章来源:http://www.cnblogs.com/gifisan ...

  9. YTU 3026: 中序线索化二叉树

    原文链接:https://www.dreamwings.cn/ytu3026/2896.html 3026: 中序线索化二叉树 时间限制: 1 Sec  内存限制: 128 MB 提交: 9  解决: ...

  10. .net 之缓存

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...