`
xiaofanqingzjj
  • 浏览: 4289 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Android Handler机制详解

阅读更多
Android Handler机制详解

消息循环

要理解Handler机制就必须先理解什么是消息循环。初学编程一般从C语言开始,C语言程序从main函数开始,执行main函数的第一行代码开始,到main的最后一行代码结束,这时候程序就运行结束了,是一个线性的执行过程。我们从以前的算法,或者数据结构方面的小程序都是通过一个main函数来驱动测试的。这时候的程序比较简单,一般没有消息循环的概念,大部分都是程序运行完了就结束了,程序运行的结果显示在屏幕上。

我们后来学习了图形用户界面,VB,DELPHI或者JAVA Swing,在图形用户界面下,程序启动后,会弹出一个与用户交互的界面,用户不对界面作操作,程序不会作响应而且永远不会退出。我们都知道程序如果是从main开始,一定会执行到main函数的最后一行代码的。但是那是什么让程序可以等待用户操作,而不执行不到main函数的最后一行代码呢?

我们或许能够隐隐约约的感觉到程序执行到某一个地方启动了一个循环,一直在等待用户操作,用户不作操作则程序一直处于循环内部等待用户发送消息,并在循环内部循环处理用户发送的消息,这就是一个典型的消息循环机制。




消息循环也是一个典型的生产者-消费者模式,生产者不停的向消息队列中生产消息,消费中不停的从消息队列中取消息,并处理消息。生产者-消费者模式是一个很典型的多线程协调的例子,在对发送消息或者取消息的时候需要消息队列做同步处理,而且当消费者向队列取消息的时候,如果没有消息,取消息线程要进入等待状态,并等待被唤醒。当生产者线程想消息队列中发送消息的时候需要唤醒当前正在等待消息队列的线程。

生产者消费者模式最主要的问题就是线程安全和线程协调。

生产者-消费着模式和线程安全

我们可以举生活中的一个例子,桌子上有一个盘子,有一个人专门往盘子里放苹果,有一个人专门从盘子里拿苹果吃,我们称放苹果的人为生产者,我们暂时取代号A,吃苹果的人为消费者,我们取其代号为B。

我们首先看吃苹果例子中的线程安全问题

A往盘子中放苹果,B往盘子中拿苹果,他们各司其责,好像没什么问题,如果A在往盘子中放苹果的过程中,B这时正好也从盘子中拿苹果。如果A正在放苹果的同时,B来拿苹果,这时可能苹果会被掰开,因为可能A的苹果还没放入盘子,B已经从盘子里那苹果了,如果这个场景是程序在运行,就可能出现程序不一致的情况了。
为什么会出现这种情况呢,是因为A和B执行任务的时候出现的了公共资源,实例中的盘子,公共资源的访问没有做任何的控制。这就是所谓的线程安全问题。线程安全问题主要是由于不同任务之间资源共享引起的。比如说例子中的盘子便是两个任务的公共资源。
解决线程安全问题
如何规避这个问题呢?我们可以把盘子变成一个带锁的盒子。盒子的旁边放一个开锁的钥匙。如果A来放苹果,他拿桌子上的钥匙打开盒子,然后带着钥匙进入盒子放苹果。当放好苹果之后再把钥匙放回桌子上。B来拿苹果的时候,首先也要拿桌子上的钥匙打开盒子,如果A正在放苹果,B是拿不到钥匙的,他知道这时候已经有人在用盒子了,所以他在盒子边上等。等钥匙被还回来的时候他才能用钥匙打开盒子,然后在盒子里面拿苹果。这样就有效的解决了上面说的多任务共享资源的问题。一般我们把多个任务中共享资源的那段代码称为临界区。

我们把盘子换成盒子,并且在盒子上加一把带钥匙的锁,成功解决了两个人吃苹果时同时访问盘子的问题,这就是所谓线程安全问题,线程安全问题就是把要使用到公共内存的代码区域称为临界区,所有进入临界区的代码都要进行加锁处理,具体代码级别的例子参看后面章节。但是我们还有个新的问题。

如果B吃苹果的速度比A放苹果的速度要快。这时B拿到钥匙进入盒子的时候,如果发现盒子里没苹果,这时候该怎么办呢?有一点我们是可以肯定,就是B不能在盒子里等,如果B在盒子里等,那么A就永远不能进入盒子放苹果了,那么B也永远吃不到苹果了,因为他拿着盒子的钥匙在盒子里边等别人放苹果。

我们考虑解决办法,B进入盒子之后发现里面没苹果。就出来把钥匙放回桌子,等别人来放苹果。这时候B该做些什么呢?B只能等待,因为B除了吃苹果已经没别的工作了。但是如果B在等待了,又有谁来通知他盒子里已经有苹果了呢?

我们可以在锁上加一个排队器,B进入盒子后,发现里面没苹果,B出来,把钥匙交还然后在锁后边排队等待有人来放苹果。A进入来放如苹果后,通知所有在锁后面排队的人去取苹果。

现在所有的流程就可以连接起来了,A每次来放苹果的时候首先拿盒子边上的钥匙,进入盒子放好苹果后,把钥匙放回原处,并通知所有正在排队的人,现在已经有苹果了。B每次来吃苹果的时候,首先拿到盒子的钥匙,然后进入盒子里拿苹果,拿到苹果后,把钥匙放回盒子边上,如果进盒子拿苹果的时候发现里面有苹果,则出来把钥匙放会盒子边上,并在 钥匙后面排队等候,等待放苹果的人来呼叫。 这种情况甚至可以是有多人同时放苹果和多个人同时吃苹果。这就是一个典型的生产者消费者模式的解决方案。

下面是一个JAVA的生产者¬-消费者的实例:
消息机制也是一个典型的生产者——消费者模式,发送消息的任务我们可以认为是上面放苹果的人A,处理消息的任务可以认为是吃苹果的人B。A每次都向制定的内存地址PUSH一个消息对象,B每次都从指定的内存获取一个消息进行处理。A和B是两个独立运行的线程。

下面开始分析android Handler机制

我们结合android源代码来详细分析android handler的实现原理和机制。Android中与handler机制相关的类有4个:Hanlder、Looper、Message、MessageQueue。

Message(消息对象,上面例子中的苹果)

Message表示handler一次要处理的的消息,Message一般聚合4个属性:what、arg1、arg2、obj。
What:用来标识消息,却分不同的消息参数
Arg1和arg2:附带的2个整型参数
Obj:如果需要附带更多更大的参数就可以放在这里

Message的对象创建非常频繁,为了节省对象的创建时间,这里使用了对象池模式来创建Message的对象。即Message提供了一个obtain()的静态方法来创建Message对象,abtain方法的内部实现的一般原理是:维护了一个固定大小的容器用来存放一些预先创建号的对象,用的时候直接返回一个对象就可以了,如果对象被使用完毕,再放会池子。一般使用recycle方法对不再使用的Message对象放会对象池。

Message对象本省也是一个链表结构。

MessageQueue(消息队列,上面例子中的盒子)

消息队列对象维护了一个线程安全的队列。有两个方法enqueueMessage和next,即向队列存入消息和从队列取出消息的方法。
这个方法可能会在不同的线程中调用,所以就会产生上面讲的吃苹果引起的并发问题(线程安全问题)。这里分析一下代码,看看JAVA是如何具体来实现生产者-消费者模式的。

在分析之前先诠释一个概念即synchronized关键字。在讲这个之前先讲讲锁的概念和条件变量的概念。

在上面吃苹果的的故事中,讲到一个临界区的概念,即一次只能有一个线程可以进入的代码区域,一般该代码区域包含了公共的资源。比如说上面吃苹果和放苹果的操作都是临界区的操作。我们讲解了加锁和加锁排队的实现。用伪代码实现如下:

放苹果:
1.给盒子开锁;(如果有人在吃苹果,是拿不到钥匙的,因为钥匙被人拿着进入盒子了,所以只能等待)
2.拿钥匙进入盒子吃苹果;
3.通知所有等待吃苹果的人可以从新取拿苹果了
4.出来把钥匙放回盒子旁边;

吃苹果:
1.给盒子开锁;(如果有人在放苹果,是拿不到钥匙的,因为钥匙别人拿了)
2.拿钥匙进入盒子吃苹果;
3.如果发现盒子里没苹果;
4.出来把钥匙放会盒子旁边,并在钥匙旁边排队,等待有人进入盒子放苹果。

JAVA代码的实现:

放苹果:
Try{
mLock.lock();
放苹果;
mLock.notifyAll();
} finally {
mLock.unlock();
}
吃苹果:
Try{
mLock.lock();
if(没苹果) {
mLock.wait();
}
吃苹果;
} finally {
mLock.unLock();
}

这就是一个典型的JAVA伪代码实现了吃苹果的场景。但是这和synchronized有什么关系呢?JAVA的所有类内部都有一个锁对象,如果一个方法被synchronized修饰,则可以认为代码如下:
Public synchronized void method() {
方法的实现;
}
等价与:
Public void method() {
Try {
对象内部锁.lock();

方法的实现;
} finally {
对象内部锁.unLock();
}
}
对象内部锁也有一个内部的条件变量,可以直接来调用对象的wait方法和notifyAll方法来使线程进入等待状态和唤醒所有等待的线程。

有了上面的分析,对MessageQueue的方法实现就很容易理解了。我们把放苹果的操作当作是向消息队列放消息,把吃苹果的操作当作消息处理。

下面贴出源代码:
放苹果,即向消息队列中放消息:

public boolean enqueueMessage(Message msg, long when) {
msg.when = when;
// 临界区
synchronized (this) {
Message p = mMessages;
printMessageQueue(p);

// 把消息插入消息队列中,并通知等待线程
if ( p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
this.notify();
} else {
while (p.next != null && p.when <= when) {
p = p.next;
}
msg.next = p.next;
p.next = msg;
this.notify();
}
}
return true;
}

吃苹果:

public Message next() {
while (true) {
long now = 0;
synchronized (this) {
now = SystemClock.uptimeMillis();

Message msg = pullNextLocked(now);
           if (msg != null) {
           return msg;
          }
}

// 临界区
synchronized (this) {
try {
if (mMessages != null) {
if (mMessages.when > now) {
this.wait(mMessages.when - now);
}


} else {
// 如果没有消息则使线程进入等待状态
this.wait();
}
} catch (InterruptedException e) {
// ignore
}
}
}
}

到这里为止就知道JAVA是如何实现生产者消费者模式了,而且如果在具体的代码中来应用。

Handler(在Android的消息机制里,放苹果和吃苹果都是同一人)

消息处理器和消息发送器
Handler即是消息的发送者,也是消息的处理者,Handler必须在消息循环线程中被创建,在创建的时候会拿到当前线程的Looper对象,这样就可以同时拿到MessageQueue对象。
public Handler() {
mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;
mCallback = null;
}

Handler发送消息即把Message放入其在创建时的MessageQueue对象。代码如下:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
boolean send = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
send = queue.enqueueMessage(msg, uptimeMillis);
}
return send;
}

Message在发送的时候注意上面标黄部分的代码,即个handler自身给了Message的target属性,即告诉Message由哪个handler来处理该消息。如何被handler调用见下面的Looper对象的实现。

Looper只管调用handler处理message的方法,具体如果来处理消息还是要handler自己来实现。Looper对象在loop()方法中会调用message.target.dispatchMessage(currentMsg)方法,即调用了Message对应的handler的dipatchMessage方法,具体的实现代码如下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
handleMessage(msg);
}
}

Handler的handleMessage(Message msg)方法,我想就是大家很熟悉的代码了,我们一般在创建handler的时候一般都会调用该方法。


Looper

消息循环

这个类才是整个消息循环的主控制类,一般要想使得一个线程变成消息循环线程的代码是这样的:

Looper.prepare();
Looper.loop();

Prepare()方法创建一个线程私有(即ThreadLocal变量)的Looper对象,Looper内部聚合了一个MessageQueue,代码如下:
private Looper() {
mQueue = new MessageQueue();
}

public static void prepare() {
sThreadLocal.set(new Looper());
}
Loop()方法使得线程进入消息循环,并永远不退出,具体的实现原理也很简单,也就是不停的检测消息队列是否为空,如果不为空则处理消息,如果消息队列为空则线程睡眠,如果有消息本放入消息队列,则唤醒线程。

具体的实现代码如下:
public static void loop() {
Looper currentLooper = myLooper();
MessageQueue queue = currentLooper.mQueue;

while (true) {
// 从消息队列中取出一个消息
Message currentMsg = queue.next();

// 处理消息
if (currentMsg != null) {
if (currentMsg.target == null) {
return ;
}

//调用Message所对应的Handler对象分发消息
currentMsg.target.dispatchMessage(currentMsg);
currentMsg.recycle();
}
}
}

Loop方法首先拿到消息队列,然后进入一个死循环,代码不断的从消息队列中取出消息,然后处理消息。
分享到:
评论
2 楼 svygh123 2016-03-13  
例子很贴切,分析的很好
1 楼 qaghan 2013-04-02  
分析很到位。

相关推荐

    android多线程handler/message机制详解

    Android 多线程Handler/Message机制详解 Android 多线程机制是 Android 应用程序中最重要的组件之一,它允许应用程序在后台执行一些操作,而不影响用户的交互体验。在 Android 中,多线程机制是基于 Handler 和 ...

    详解Android Handler 机制 (一)用法全解

    ps:这是关于Android Handler 机制的第一篇文章,主要来说一下Handler的用法,本文尽量归纳完全,如有缺漏,欢迎补充。 Handler的主要作用是切换线程,以及隐式的充当接口回调的作用,当子线程网络请求结束后,通过...

    android handler

    ### Android Handler机制详解 #### 一、概述 在Android开发中,`Handler`是一个非常重要的概念,它主要用于解决UI线程和其他线程之间的通信问题。本文将深入探讨Android中的`Handler`机制及其与线程、`Looper`以及...

    android handler理解

    ### Android Handler机制详解 #### 一、Handler的基本概念与作用 **Handler** 是Android开发中一个非常重要的组件,主要用于在不同线程之间传递消息。它主要用于解决子线程与主线程之间的通信问题,使得子线程能够...

    Handler机制详解

    Handler机制详解 Handler机制是Android系统中用于线程间通信的一种关键机制,它与MessageQueue和Looper紧密协作,实现消息的传递与处理。在Android应用的主线程中,每个应用程序都有一个默认的Looper,它持续不断地...

    Android handler message奇怪用法详解

    在Android开发中,多线程消息处理机制是关键的一环,尤其当涉及到UI更新和后台任务同步时。本文将深入探讨一个特殊的、不常见的Handler用法,这些用法可能在日常开发中不太常见,但对理解和优化代码逻辑具有重要意义...

    Android开发工程师面试题之handler详解。android程序员,android开发面试资料,详解

    ### Android开发工程师面试题之Handler详解 在Android开发过程中,Handler是进行线程间通信的重要机制之一,尤其在实现...在实际开发中合理运用Handler机制,不仅能有效提高程序的性能,还能使代码更加优雅和高效。

    Android消息机制Handler的工作过程详解

    总结总结 Android的消息机制Handler是线程间通信的关键工具,它允许开发者在后台线程执行耗时操作,而将UI更新交由主线程处理,保证了UI操作的安全性和性能。理解Handler、Looper和MessageQueue的工作原理以及它们...

    ZX-Android-Android消息机制详解

    总结起来,Android的消息机制是通过Looper、Handler和MessageQueue协同工作,实现了线程间的同步通信,特别是在UI更新和异步任务处理方面发挥着至关重要的作用。理解和熟练掌握这一机制,对于开发高效、响应迅速的...

    Android Handler运行原理

    在Android开发中,线程间通信是非常重要的技术之一,其中Handler机制被广泛应用于实现主线程与子线程之间的数据交互。Handler机制不仅简单易用,而且功能强大,是Android开发者必须掌握的核心技术之一。本文将深入...

    Android Handler的详解及实例

    以下是对Android Handler的详解及其正确使用的实例。 首先,理解Handler的基本概念。Handler通常与Looper和Message一起工作,它们构成了Android中线程间通信的核心机制。Looper是在一个线程中运行的消息循环,负责...

    详解Android中Handler的内部实现原理

    总结来说,Handler机制是Android中实现多线程通信的重要手段,通过理解其内部实现原理,开发者可以更好地利用这一工具,优化应用程序的性能和用户体验。在编程实践中,深入理解Handler、Looper和MessageQueue之间的...

    Handler和looper详解

    Handler和Looper是Android系统中两个非常重要的组件,它们之间相互关联,共同实现了Android系统中的消息处理机制。在本文中,我们将对Handler和Looper进行详细的解释,并探讨它们在Android系统中的作用。 一、...

    Android Handler 机制实现原理分析

    Android Handler机制是Android异步消息处理的核心,它用于在主线程和子线程之间进行通信,确保UI更新在主线程中执行。以下是对Handler机制实现原理的详细解析: 1. **Handler**: Handler类是消息处理的中心,它有两...

    Handler消息处理机制+面试说.md

    ### Handler消息处理机制详解 #### 一、原理 在Android开发中,`Handler`机制是进行线程间通信的关键技术之一。它通过一系列组件(包括`Handler`、`Looper`和`MessageQueue`)实现了一个异步消息队列,允许开发者...

    Android优化技术详解

    理解Looper、Handler和Message机制,以及它们在多线程交互中的作用,也是必要的。 接下来是资源优化。使用SVG格式的图形资源替代位图,可以降低APK大小并提高分辨率适应性。对于音频和视频,压缩和优化编码格式能...

    Android应用开发详解.pdf

    4. **多线程与异步处理**:了解Handler、AsyncTask等机制,实现耗时操作不在UI线程中执行。 5. **权限管理**:熟悉Android权限模型,学会正确请求和使用权限。 ### Android应用性能优化 1. **内存优化**:避免内存...

    Android Handler 原理分析及实例代码

    在Android开发中,`Handler` 是一个至关重要的组件,它涉及到多线程、UI更新以及消息传递机制。本文将深入探讨 `Handler` 的工作原理,分析其如何在不同线程间进行通信,并通过实例代码来展示其具体用法。 ### ...

    Android应用开发详解源码全

    源码可能包含Handler、Looper、AsyncTask等机制,理解这些可以优化应用的并发性能。 第七,权限管理是Android系统的一个关键特性。从Android 6.0(API级别23)开始,运行时权限引入,源码分析可以帮助我们更好地...

Global site tag (gtag.js) - Google Analytics