来自:http://blog.csdn.net/andyhuabing/article/details/7368217

Windows编程的朋友可能知道Windows程序是消息驱动的,并且有全局的消息循环系统。而Android应用程序也是消息驱动的,按道理来说也应该提供消息循环机制。Android通过Looper、Handler来实现消息循环机制,Android消息循环是针对线程的(每个线程都可以有自己的消息队列和消息循环)。

在 Android 系统 ,这些工作由由由Looper 及 Handler 来完成。

先分析Looper类:
主要提供负责线程的消息循环和消息队列

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();  // 进入消息循环
        }
    }

下面Looper类的准备函数:
private static final ThreadLocal sThreadLocal = new ThreadLocal();

public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }

这里利用 ThreadLocal 线程局部存储变量将将Looper与调用线程关联起来。
而消息处理流程如何呢?
    /**
     *  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();  // 线程中存储的Looper对象
        MessageQueue queue = me.mQueue;  // Looper类中的消息队列
        while (true) {
            Message msg = queue.next(); // might block   获取消息
            //if (!me.mRun) {
            //    break;
            //}
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }
                if (me.mLogging!= null) me.mLogging.println(
                        ">>>>> Dispatching to " + msg.target + " "
                        + msg.callback + ": " + msg.what
                        );
                msg.target.dispatchMessage(msg);  // 利用 Target 注册的方法处理消息
                if (me.mLogging!= null) me.mLogging.println(
                        "<<<<< Finished to    " + msg.target + " "
                        + msg.callback);
                msg.recycle(); 
            }
        }
    }
    
可以通过Loop.myLooper()得到当前线程的Looper对象,通过Loop.getMainLooper()可以获得当前进程的主线程的Looper对象。

在android系统,UI线程就是消息的主线程,在 ActivityThread.java 中创建:
public static final void main(String[] args) {
     Looper.prepareMainLooper();
     
     Looper.loop(); //消息循环处理
}

-->
    public static final void prepareMainLooper() {
        prepare();
        setMainLooper(myLooper());
        if (Process.supportsProcesses()) {
            myLooper().mQueue.mQuitAllowed = false;
        }
    }

private synchronized static void setMainLooper(Looper looper) {
        mMainLooper = looper;
    }
    
再来分析 Handler 类:
主要提供负责将消息加入到特定的Looper消息队列中,并分发和处理访消息队列中的消息,构造Handler的时候可以指定一个Looper对象,如果不指定则利用当前线程的Looper创建。

请看如下代码即可明白:
    public Handler(Looper looper, Callback callback) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
    }
    
这里也同时告诉我们将消息放入到主线程的消息队列中,只需要创建Hanlde对象时以主线程的Looper创建即可。则sendMessage及handleMessage都会在主线程中进行处理。

再看如下变量:
    final MessageQueue mQueue;
    final Looper mLooper;
    final Callback mCallback;

这里也有 队列,Looper对象及回调函数类,通过 Handler 不同构造函数完成相应的操作。
简化使用队列及消息传递的复杂性,提供方便的调用方法。其中最重要的函数是:

/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);  // 1、利用 Calback 函数处理消息
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) { // 2、利用mCallback处理消息
                    return;
                }
            }
            handleMessage(msg); // 3、利用子类处理消息,这里最常用的方法,直接重载handleMessage函数
        }
    }
    
那么这几者的关系是怎么样的呢?

一个Activity中可以创建多个工作线程或者其他的组件,如果这些线程或者组件把他们的消息放入Activity的主线程消息队列,那么该消息就会在主线程中处理了。

还有一个问题就是Looper和Handler的同步关系如何处理,在android由HandlerThread类进行解决了。

public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait(); // 如果新线程还未创建Looper对象,则等待
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

新线程创建运行run函数创建Looper对象:
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();// 这里会创建程的Looper对象
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll(); // ok,创建好了则通知,最后调用Looper.loop()进入消息循环
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

这里利用 notifyAll / wait 轻检解决此问题了,所以多多使用用HanlderThread类完成多线程同步问题吧。

培训时学员提出的疑问进行补充说明:

每个线程都可以有自己的消息队列和消息循环,一个线程可以存在(当然也可以不存在)一个消息队列和一个消息循环(Looper),特定线程的消息只能分发给本线程,不能进行跨线程,跨进程通讯。
   
那么除了UI thread(ActivityThread main Looper线程)工作如何运行的呢?
可以参考下 AsyncQueryHandler.java 实现:

private static Looper sLooper = null;

private Handler mWorkerThreadHandler;

// 工作线程,处理消息,减轻UI thread负担
    protected class WorkerHandler extends Handler {
      public WorkerHandler(Looper looper) {
           super(looper);
      }    
    @Override
    public void handleMessage(Message msg) {
    ...
    }
    }
    
    //1、构造Looper消息处理线程HandlerThread
    public AsyncQueryHandler(ContentResolver cr) {
        super();
        mResolver = new WeakReference<ContentResolver>(cr);
        synchronized (AsyncQueryHandler.class) {
            if (sLooper == null) {
                HandlerThread thread = new HandlerThread("AsyncQueryWorker");

// 这里将会调用到HandlerThread.run()进入到Looper.loop();循环消息处理中

thread.start();

//2、利用thread.getLooper()获取自已的Looper

sLooper = thread.getLooper();
            }
        }
        //3、并设定给WorkerHandler进行构造绑定其Looper对象
        mWorkerThreadHandler = createHandler(sLooper);
    }

protected Handler createHandler(Looper looper) {
        return new WorkerHandler(looper);
    }
    
    //4 利用mWorkerThreadHandler(Handler对象)发送消息
    public void startQuery(int token,...){

Message msg = mWorkerThreadHandler.obtainMessage(token);

msg.arg1 = EVENT_ARG_QUERY;

...

msg.obj = args;

mWorkerThreadHandler.sendMessage(msg);

}

内容补充:

Android的消息循环是针对线程的,每个线程都可以有自己的消息队列和消息循环。

Android系统中的Looper负责管理线程的消息队列和消息循环。通过Looper.myLooper()得到当前线程的Looper对象,通过Looper.getMainLooper()得到当前进程的主线程的Looper对象。

消息队列(MessageQueue)和消息循环(Looper),但是我们看到每个消息处理的地方都有Handler的存在,它是做什么的呢?

Handler的作用是把消息加入特定的Looper所管理的消息队列中,并分发和处理该消息队列中的消息。

构造Handler的时候可以指定一个Looper对象,如果不指定则利用当前线程的Looper对象创建。

多个子线程访问主线程的Handler对象,Handler对象管理的Looper对象是线程安全的,不管是添加消息到消息队列还是从消息队列中读取消息都是同步保护的,所以不会出现数据不一致现象。

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

  1. 转 Android的消息处理机制(图+源码分析)——Looper,Handler,Message

    作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习google大牛们的设计思想.android源码中包含了大量的设计模式,除此以外,android sdk还精心为我们设计了各种 ...

  2. 【转】android的消息处理机制(图+源码分析)——Looper,Handler,Message

    原文地址:http://www.cnblogs.com/codingmyworld/archive/2011/09/12/2174255.html#!comments 作为一个大三的预备程序员,我学习 ...

  3. android的消息处理机制(图+源码分析)——Looper,Handler,Message

    android源码中包含了大量的设计模式,除此以外,android sdk还精心为我们设计了各种helper类,对于和我一样渴望水平得到进阶的人来说,都太值得一读了.这不,前几天为了了解android ...

  4. 《Android进阶》之第三篇 深入理解android的消息处理机制

    Android 异步消息处理机制 让你深入理解 Looper.Handler.Message三者关系 android的消息处理机制(图+源码分析)——Looper,Handler,Message an ...

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

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

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

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

  7. android的消息处理机制——Looper,Handler,Message

    在开始讨论android的消息处理机制前,先来谈谈一些基本相关的术语. 通信的同步(Synchronous):指向客户端发送请求后,必须要在服务端有回应后客户端才继续发送其它的请求,所以这时所有请求将 ...

  8. Android异步消息处理机制

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

  9. android基础(六)android的消息处理机制

    Android中的消息处理机制由四个部分组成:Message.Handler.MessageQueue和Looper,并且MessageQueue封装在Looper中,我们一般不直接与MQ打交道. 一 ...

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

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

随机推荐

  1. WPF学习笔记 控件篇 属性整理【1】FrameworkElement

    最近在做WPF方面的内容,由于好多属性不太了解,经常想当然的设置,经常出现自己未意料的问题,所以感觉得梳理下. ps:先补下常用控件的类结构,免得乱了 .NET Framework 4.5 Using ...

  2. URL地址下载图片到本地

    package test.dao; import eh.base.dao.DoctorDAO; import eh.entity.base.Doctor; import junit.framework ...

  3. MySQL 多实例数据库还原脚本-备份集与端口对应

    版本:5.5.14 OS: ConetOS 6.3 1.创建recover.sh [root@yoon  export]# vi  recover.sh #!/bin/bash bakdir=/exp ...

  4. ASP.NET 运行机制续(完结)

    上一篇说到applicationInstance会执行一些列的事件.下面是我在msdn上找到有关asp.net程序生命周期相关的描述及图片 声明周期的起始 ASP.NET 应用程序的生命周期以浏览器向 ...

  5. Java线程同步(synchronized)——卖票问题

    卖票问题通常被用来举例说明线程同步问题,在Java中,采用关键字synchronized关键字来解决线程同步的问题. Java任意类型的对象都有一个标志位,该标志位具有0,1两种状态,其开始状态为1, ...

  6. mysql_fetch_row,mysql_fetch_array,mysql_fetch_assoc的区别

    <?php $link=mysql_connect('localhost','root',”); mysql_select_db('abc',$link); $sql = “select * f ...

  7. C#中Linq查询基本操作

    摘要:本文介绍Linq查询基本操作(查询关键字) - from 子句 - where 子句 - select子句 - group 子句 - into 子句 - orderby 子句 - join 子句 ...

  8. 3143: [Hnoi2013]游走 - BZOJ

    Description 一个无向连通图,顶点从1编号到N,边从1编号到M. 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点, ...

  9. 输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。

    // test20.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> #include< ...

  10. 【BZOJ】【2179】FFT快速傅里叶

    FFT 做的第二道用到FFT的……好吧其实还是模板题-_-b 百度上说好像分治也能做……不过像FFT这种敲模板的还是省事=.= /*********************************** ...