Android的消息循环机制 Looper Handler类分析

Looper类说明

   Looper 类用来为一个线程跑一个消息循环。

  线程在默认情况下是没有消息循环与之关联的,Thread类在run()方法中的内容执行完之后就退出了,即线程做完自己的工作之后就结束了,没有循环的概念。

  调用Looper类的 prepare() 方法可以为当前线程创建一个消息循环,调用loop() 方法使之处理信息,直到循环结束。

  大多数和消息循环的交互是通过 Handler 类进行的。

  下面是一个典型的实现:

  class LooperThread extends Thread {
public Handler mHandler; public void run() {
Looper.prepare(); mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
}; Looper.loop();
}
}

Handler类说明  

  Handler类用来发送和处理消息(Message)以及和线程的消息队列(MessageQueue)关联的Runnable对象。

  每一个Handler对象都仅和一个线程及这个线程的消息队列关联。

  一个特定线程的所有Handler对象都会收到同样的方法。(这是一个“一对多”的关系)。

  当你创建一个新的Handler对象,它会和创建它的这个线程/线程的消息队列绑定,从那个时刻开始,它将向这个消息队列传递消息和runnable对象,并且当它们从队列中出来时执行它们。

  Handler主要有两种用途:

  1.合理调度安排消息和runnable对象,使它们在将来的某个点被执行。

  2.将一个动作入队安排在非当前线程执行。

  调度消息是通过一系列的post方法和sendMessage方法。

  post方法允许你向消息队列中入队一些Runnable对象,在它们被接收到的时候会被调用,(实际上post方法也就是将runnable对象包装在消息里,然后再通过sendMessage方法实现),post方法有:

  post(Runnable r)

  postAtFrontOfQueue(Runnable r)

  postAtTime(Runnable r, Object token, long uptimeMillis)

  postAtTime(Runnable r, long uptimeMillis)

  postDelayed(Runnable r, long delayMillis)

  sendMessage方法允许你入队一个消息对象(Message),包含一个bundle数据,之后将会被Handler的handleMessage(Message)方法所处理。

  (这个需要你实现一个Handler的子类)。

  sendMessage方法有:

  sendEmptyMessage(int what)

  sendEmptyMessageAtTime(int what, long uptimeMillis)

  sendEmptyMessageDelayed(int what, long delayMillis)

  sendMessage(Message msg)

  sendMessageAtFrontOfQueue(Message msg)

  sendMessageAtTime(Message msg, long uptimeMillis)

  sendMessageDelayed(Message msg, long delayMillis)

  一个线程对应一个Looper,有一个消息队列,但是可以关联多个Handlers。

UI线程和非UI线程的通信

  当你的应用进程被创建的时候,应用进程的主线程(main thread)就建立一个消息队列,操纵top级别的应用对象(比如activities、broadcast receivers等)和它们创建的任何窗口。

  因为效率的考虑,所有的View和Widget都不是线程安全的,所以相关操作强制放在同一个线程,这样就可以避免多线程带来的问题。这个线程就是主线程,也即UI线程。

  你可以创建自己的线程,通过一个Handler对象和应用的主线程通信。

  如果你将一个Handler和你的UI线程连接,处理消息的代码就将会在UI线程中执行。

  新线程和UI线程的通信是通过从你的新线程调用和主线程相关的Handler对象的post或者sendMessage方法实现的,给定的Runnable或Message将会在Handler的消息队列中,并且在合适的时间被处理。

  总的来说,共有5种方式从非UI线程和UI线程通信:

  还有就是通过Handler,或者使用AsyncTask。

  具体参见之前的博文:http://www.cnblogs.com/mengdd/p/3418780.html

消息循环

  消息处理机制中,消息存放在一个消息队列中,而线程围绕这个队列进入一个无限循环,直到程序退出。

  如果队列中有消息,线程就会把消息取出来,并分发给相应的Handler进行处理;

  如果队列中没有消息,线程就会进入空闲等待状态,等待下一个消息的到来。

Android的主线程循环创建

  Android程序的运行入口点可以认为是android.app.ActivityThread类的main()方法(源码2.3.3):

    public static final void main(String[] args) {
// other codes... // 创建主线程循环
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
} ActivityThread thread = new ActivityThread();
thread.attach(false); // other codes... // 进入当前线程(此时是主线程)消息循环
Looper.loop(); // other codes... thread.detach();
// other codes...
}

  这个main()方法里面为程序创建了主线程循环

  Looper类中的主线程创建方法prepareMainLooper()

    /**
* Initialize the current thread as a looper, marking it as an application's
* main looper. The main looper for your application is created by the
* Android environment, so you should never need to call this function
* yourself. {@link #prepare()}
*/ public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
// other codes...
}

  上面这个方法是专门为创建应用程序的主线程调用的,其他线程都不应该调用这个方法,而应该调用prepare()方法。

  主线程的Looper对象创建好之后会存在Looper类的成员变量mMainLooper里,通过一个get方法可以获取到:

    /**
* Returns the application's main looper, which lives in the main thread of
* the application.
*/
public synchronized static final Looper getMainLooper() {
return mMainLooper;
}

  这样之后,程序中其他线程就可以获取主线程的消息循环对象,从而和主线程通信。

线程创建消息循环:Looper.prepare()

  非主线程创建消息循环时,调用的是Looper类的prepare()方法,其实创建主线程的方法实质也调用了prepare方法:

    /**
* Initialize the current thread as a looper. This gives you a chance to
* create handlers that then reference this looper, before actually starting
* the loop. Be sure to call {@link #loop()} after calling this method, and
* end it by calling {@link #quit()}.
*/
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException(
"Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}

  这个方法会调用Looper类的私有构造方法,创建Looper类对象。

    private Looper() {
// 私有构造方法,在prepare()方法里面调用
// 创建消息队列
mQueue = new MessageQueue();
mRun = true;
// 当前线程
mThread = Thread.currentThread();
}

进入消息循环:Looper.loop()

  不管是不是主线程,prepare之后需要调用Looper类的loop()方法,可以看作是进入消息循环:

/**
* Run the message queue in this thread. Be sure to call {@link #quit()} to
* end the loop.
*/
public static final void loop() {
// 进入当前线程的消息循环
Looper me = myLooper();
MessageQueue queue = me.mQueue;
while (true) { // 从队列中取出消息
Message msg = queue.next(); // might block if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
// other codes... // 分发消息
msg.target.dispatchMessage(msg);
// 消息的target是Handler类型的对象 // other codes... // 释放清理
msg.recycle();
}
}
}

消息分发和处理——Handler

  前面创建了消息循环,并且进入了这个循环,但是消息队列中的消息是如何加入和处理的呢?是通过Handler。

Handler构造:

  Handler有几个构造重载,如果构造时不提供Looper类对象参数,会获取当前线程的Looper对象,即将当前线程的消息循环作为Handler关联的消息循环。

  前面说过,不是所有线程都有一个消息循环,所以如果当前线程没有消息循环,而构造Handler对象时又没有指定Looper对象,则会抛出一个运行时异常

        mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}

  如果没有抛出异常,Handler对象构造好之后,它就关联了相应的Looper实例和消息队列实例,即完成绑定。

消息发送:

  Handler对象的post方法和sendMessage方法本质上都是发送消息的方法(post类方法实质上是调用了sendMessage方法)。

  所谓发送消息就是把消息放入消息队列中的合适位置,并且把消息的target设置为本Handler对象

  (这里将消息加入队列,也有一些什么线程唤醒的事儿咱们不深入讨论了)。

  可以添加,也就相应地有一些移除方法。

消息处理:

  在上面的Looper.loop()方法中,调用了消息对象target(即发送这个消息的Handler对象)的dispatchMessage()方法。

   /**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) { // 首先,处理Message自己的callback,调用其run方法
if (msg.callback != null) {
handleCallback(msg);
}
else {
// 其次,调用Handler自留的接口对象
// 这个成员变量声明时的注释如下:
/**
* Callback interface you can use when instantiating a Handler to
* avoid having to implement your own subclass of Handler.
*/
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
} // 最后,调用handleMessage方法处理消息,Handler类中这个方法为空,子类可以重写这个方法
handleMessage(msg);
}
}

  Handler类的handleMessage()方法默认实现为空:

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

  上面的代码中也解释了为什么一个消息队列可以关联很多个Handler对象,因为虽然队列只有一个,但是消息的target是当时把它加入的Handler对象。

  所以当队列中的消息处理的时候,也会找到当时送它来的Handler对象,调用其相应的dispatchMessage()方法,进而调用其中的handleMessage()方法或者mCallback成员的handleMessage()方法来进行处理。

参考资料

  Handler:http://developer.android.com/reference/android/os/Handler.html

  Looper:http://developer.android.com/reference/android/os/Looper.html

  比较好的几个博文:

  Android应用程序线程消息循环模型分析:http://blog.csdn.net/luoshengyang/article/details/6905587

  Android应用程序消息处理机制(Looper、Handler)分析:http://blog.csdn.net/luoshengyang/article/details/6817933

  Android的消息队列模型:http://www.cnblogs.com/ghj1976/archive/2011/05/06/2038469.html

  Android中的Handler, Looper, MessageQueue和Thread:http://www.cnblogs.com/xirihanlin/archive/2011/04/11/2012746.html

  本博客其他相关博文:

  Android中的UI线程与非UI线程:http://www.cnblogs.com/mengdd/p/3418780.html

  说明:本文相关源码是Android 2.3.3版本的。

Android的消息循环机制 Looper Handler类分析的更多相关文章

  1. Android HandlerThread 消息循环机制之源代码解析

    关于 HandlerThread 这个类.可能有些人眼睛一瞟,手指放在键盘上,然后就是一阵狂敲.立即就能敲出一段段华丽的代码: HandlerThread handlerThread = new Ha ...

  2. 「Android」消息驱动Looper和Handler类分析

    Android系统中的消息驱动工作原理: 1.有一个消息队列,可以往这个消息队列中投递消息; 2.有一个消息循环,不断的从消息队列中取得消息,然后处理. 工作流程: 1.事件源将待处理的消息加入到消息 ...

  3. Android Handler 消息循环机制

    前言 一问起Android应用程序的入口,很多人会说是Activity中的onCreate方法,也有人说是ActivityThread中的静态main方法.因为Java虚拟机在运行的时候会自动加载指定 ...

  4. 安卓中的消息循环机制Handler及Looper详解

    我们知道安卓中的UI线程不是线程安全的,我们不能在UI线程中进行耗时操作,通常我们的做法是开启一个子线程在子线程中处理耗时操作,但是安卓规定不允许在子线程中进行UI的更新操作,通常我们会通过Handl ...

  5. 【转】Android开发实践:自定义带消息循环(Looper)的工作线程

    http://ticktick.blog.51cto.com/823160/1565272 上一篇文章提到了Android系统的UI线程是一种带消息循环(Looper)机制的线程,同时Android也 ...

  6. Android中消息系统模型和Handler Looper

    http://www.cnblogs.com/bastard/archive/2012/06/08/2541944.html Android中消息系统模型和Handler Looper 作为Andro ...

  7. Android多线程----异步消息处理机制之Handler详解

    ​[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...

  8. Dart异步与消息循环机制

    Dart与消息循环机制 翻译自https://www.dartlang.org/articles/event-loop/ 异步任务在Dart中随处可见,例如许多库的方法调用都会返回Future对象来实 ...

  9. 【Dart学习】-- Dart之消息循环机制[翻译]

    概述 异步任务在Dart中随处可见,例如许多库的方法调用都会返回Future对象来实现异步处理,我们也可以注册Handler来响应一些事件,如:鼠标点击事件,I/O流结束和定时器到期. 这篇文章主要介 ...

随机推荐

  1. js IndexedDB:浏览器端数据库的demo实例

    IndexedDB具有以下特点. (1)键值对储存. IndexedDB内部采用对象仓库(object store)存放数据.所有类型的数据都可以直接存入,包括JavaScript对象.在对象仓库中, ...

  2. go环境import cycle not allowed问题处理

    1.前言 今天在搭建Go语言环境,从https://golang.org/dl/上下载一个Go语言环境.环境变量配置刚开始如下配置: GOROOT=/home/go/bin     go执行文件目录 ...

  3. Android APP压力测试(一)之Monkey工具介绍

    Android APP压力测试(一) 之Monkey工具介绍 前言 本文主要介绍Monkey工具.Monkey测试是Android平台自动化测试的一种手段,通过Monkey程序模拟用户触摸屏幕.滑动. ...

  4. Microsoft Azure News(2) 在Microsoft Azure上运行SAP应用程序

    <Windows Azure Platform 系列文章目录> 2014年6月27日消息,在 Microsoft Azure 上提供 SAP Business Suite 软件.All-I ...

  5. WordPress主题模板层次和常用模板函数

    首页: home.php index.php 文章页: single-{post_type}.php – 如果文章类型是videos(即视频),WordPress就会去查找single-videos. ...

  6. C#简单问题,不简单的原理:不能局部定义自定义类型(不含匿名类型)

    今天在进行代码测试时发现,尝试在一个方法中定义一个委托,注意是定义一个委托,而不是声明一个委托变量,在编写的时候没有报错,VS也能智能提示,但在编译时却报语法不完整,缺少方括号,但实际查询并没有缺少, ...

  7. 语义化HTML:p、h1-6、q、blockquote、hr、address、code、pre、var、cite、dfn和samp

    一.元素语义 p标签 W3C草案: The p element represents a paragraph.W3C specification 语义化的 <p>元素 表示:文章中的段落. ...

  8. 开源服务专题之--------mysql的编译安装

    为什么选择MySQL 1:mysql性能卓越,服务稳定,很少出现异常宕机 2:mysql开源免费,无版权制约,自主性及使用成本低 3:产品耦合度,mysql支持多种操作系统,支持多开发语言,特别是ph ...

  9. 提高生产性工具 - Model代码生成器(NET / JAVA) (一)

    原来在上一家公司,整整一年都在做工具,提高生产性,那个项目特别巨大,所以总共为老东家节约了500K左右的美金. (除了表扬之外,我个人什么好处都没有,领导们都升官发财了,郁闷) 到了新公司,也准备开发 ...

  10. MySQL带参数的存储过程小例子

    http://wwty.iteye.com/blog/698239 mysql存储过程也提供了对异常处理的功能:通过定义HANDLER来完成异常声明的实现 语法如下: DECLARE handler_ ...