Android的消息处理机制——Looper,Handler和Message浅析

题外话:

说来有些惭愧,对于这三者的初步认识居然是在背面试题的时候。那个时候自己接触Android的时间还不长,学习的书籍也就是比较适合入门的《疯狂Android讲义》,当然在学到Handler这一部分的时候,书中也是有提到一些简单示例,后来在工作中需要用到这个MessageQueue的时候才开始真正琢磨了一下这三者的联系。如果想要对这三者好好理解一番,个人还是比较推荐《深入理解Android卷Ⅰ》。以下对这三者之间的恩怨纠葛的介绍和分析也是参考这本书的相关章节,算是一篇读书笔记吧。

概述:

Android的消息传递机制是另一种形式的“事件处理”,这种机制主要是为了解决Android应用中的多线程问题——Android平台只允许UI线程修改Activity中的UI组件,这就使得新启动的线程无法去动态修改界面组件中的属性值。但是我们的程序界面不可能是一个静态的呈现,所以这就必须用到本博客中提到的三个大类了。

简单示例:

代码展示:

public class LooperThreadActivity extends Activity {

    private final int MSG_HELLO = 0;
    private Handler mHandler;
    private CustomThread mThread = null;
    private static final String TAG = LooperThreadActivity.class.getSimpleName();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mThread = new CustomThread();
        mThread.start();

        Button sendButton = (Button) findViewById(R.id.send_button);
        final EditText contentEditText = (EditText) findViewById(R.id.content_edittext);

        sendButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                String msgText = contentEditText.getText().toString();
                sendMessage(msgText);
            }
        });
    }

    private void sendMessage(String content) {
        Toast.makeText(this, "send msg: " + content, 0).show();

        // TODO 1.向MessageQueue中添加消息

        // 通过Message.obtain()来从消息池中获得空消息对象,以节省资源
        Log.i(TAG, "------------> send msg 1.");
        Message msg = mHandler.obtainMessage(MSG_HELLO, content);
        msg.sendToTarget();

        Log.i(TAG, "------------> send msg 2.");
        Message msg2 = mHandler.obtainMessage(MSG_HELLO, content + "2");
        msg2.sendToTarget();
    }

    class CustomThread extends Thread {
        @Override
        public void run() {
            Looper.prepare();
            Log.i(TAG, "------------> loop.pre.");

            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                    case MSG_HELLO:
                        Log.i(TAG, "------------> receive msg.");
                        Toast.makeText(LooperThreadActivity.this, "receive msg: " + (String) msg.obj, 0).show();
                    }
                }
            };
            Looper.loop();
        }
    }
}

运行效果展示:

 

Log结果展示:

示例结果分析:

大家可以看到我做了连续两次的添加消息数据,在结果中也有很好的体现,不过Looper.prepare();和Handler之间的内容却只执行了一次。这是因为我们自定义的线程CustomThread只被start了一次,且start过后一直存在,没有被销毁,所以Looper一直存在,MessageQueue一直存在,从而保证了一个Thread只能有一个Looper对象。对于这一点下面会用源码进行进一步的说明。

机制浅析:

就应用程序而言,Android系统中Java的应用程序和其他系统上相同,都是靠消息驱动来工作的。Android系统中的消息驱动离不开Looper、Handler和Message这三者,虽说不上哪个更重要一些,不过相对突出的的确是Looper。下面就对这些类逐一地介绍。

Looper类分析:

1.Looper.prepare();

跟踪prepare()进入Android的源码,我们可以发现以下源代码:

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

sThreadLocal定义:

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

从以上源码中我们可以看到,在调用prepare的线程中,设置了一个Looper对象,这个Looper对象就保存在这个调用线程的TLV中。而Looper对象内部封装了一个消息队列。也就是说prepare通过ThreadLocal机制,把Looper和调用线程关联在了一起。

2.Looper.loop();

跟踪loop()进入Android的源码(此处删除了一些暂时不太关联的代码):

public static void loop() {
	final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        msg.target.dispatchMessage(msg);

        msg.recycle();
    }
}

通过上面的分析,Looper有以下几个作用:

  • 封装了一个消息队列.
  • prepare函数把当前的Looper和调用prepare的线程(即最终的处理线程)绑定在了一起.
  • 处理线程调用loop,处理来自该消息队列中的消息.

当事件源向这个Looper发送消息的时候,其实就是把消息加到这个Looper的消息列队里了。那么,该消息就将由和Looper绑定的处理来处理。

Handler类分析:

学习Handler之初先来认识一下Handler中所包含的部分成员:

final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;

在Handler类中,它的构造函数会把Handler中的消息队列变量最终都会指向Looper的消息队列。由于是被指向,那么Handler中的消息队列其实就是某个Looper的消息队列。

Looper和Handler的同步关系说明及HandlerThread的介绍:

Looper和Handler之间其实是存在着同步关系的。这里对它们之间的同步关系不做过多介绍,如果想了解可以参看《深入理解Android卷Ⅰ》第128页。笔者在此只提出一个提醒点:由于HandlerThread完美地解决了Looper和Handler同步过程中可能出现的空指针异常问题,所以在以后的开发过程中,我们还是多用HandlerThread吧。当然如果不想使用它,那就请使用锁机制来健壮你的代码吧,不过这就可能会落下重复造轮子的口舌了。

参考资料:

1.《疯狂Android讲义》

2.《深入理解Android卷Ⅰ》

3.网络资源:http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html

4.网络资源:http://www.cnblogs.com/bastard/archive/2012/06/08/2541944.html

5.网络资源:http://blog.csdn.net/mylzc/article/details/6771331

尾声:

虽然已经“稀里糊涂”到了结尾,不过对Looper、Handler和Message的认识的确进了一大步。希望看完本文的你也有所收获。

时间: 01-28

Android的消息处理机制——Looper,Handler和Message浅析的相关文章

android线程消息传递机制——Looper,Handler,Message

android线程消息传递机制——Looper,Handler,Message 在引入这些概念之前,我们先了解一下引入这些机制的背景. 出于性能优化的考虑,Android的UI操作并不是线程安全的(如果你不懂什么是线程安全,可以阅读一下<一起探究多进程与多线程>里的数据安全与可重入),这意味着如果有多个线程同时操作某个UI组件,可能导致线程安全问题.为了解决这个问题,Android制定了一条简单的规则:只允许UI线程修改Activity里的UI组件.这个UI线程也通常被我们称为主线程. 在此引

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

这篇文章有一半是copy别人的,站在巨人的肩膀上,我们才能看得更高更远...... 在开始讨论android的消息处理机制前,先来谈谈一些基本相关的术语. 通信的同步(Synchronous):指向客户端发送请求后,必须要在服务端有回应后客户端才继续发送其它的请求,所以这时所有请求将会在服务端得到同步,直到服务端返回请求. 通信的异步(Asynchronous):指客户端在发送请求后,不必等待服务端的回应就可以发送下一个请求. 所谓同步调用,就是在一个函数或方法调用时,没有得到结果之前,该调用就

Android 消息处理机制-Looper,Handler,MessageQueue

先膜拜下大神的帖子,从源码的角度分析android的Handler机制.   链接:android的消息处理机制(图+源码分析)——Looper,Handler,Message 这里就不赘言,仅仅做一些介绍,想看详细请猛戳上面的链接. android的消息机制由三块组成Looper,Handler,MessageQueue.我们知道在android中子线程中是不能够执行组件的更新操作的,既然这样我们只能在子线程中把消息发送给主线程,让主线程自己去更新,这里就会用到Handler.子线程通过调用主

【Android 开发】: Android 消息处理机制之一: Handler 与 Message

最近几讲内容,我们学习了Android中关于多线程的一些知识,上一讲我们讲解了异步任务 AsyncTask 的操作,Android中还提供了其他的线程操作,如Handler Message MessageQueue Looper 等模块,这些就是Android中的消息处理机制.这部分内容是Android学习过程中的重点和难点.    现在我们就来学习一下Android的消息处理,以及剖析一下相关类如Handler和Message类的源代码,同时使用他们来更新UI主线程的操作.因为Android的

Android 异步消息处理机制Looper、Handler、Message三者关系

Looper,Handler,Message三者是我们常常用来再子线程跟新UI的,我们把Message发给Handler,然后,handler调用HandlerMessage()方法,我们在这个方法里面更新UI.那么Looper呢,又是什么,下面我来给大家介绍一下三者的关系. 首先要说明的是,每个线程最多只有一个Looper,在线程里面调用Looper.prepare()就是为这个线程设置了一个Looper,所以在子线程,我们使用这个三者前,一定要调用Looper.prepare()方法,而主线

[Android] 异步消息处理机制(Handler 、 Looper 、MessageQueue)源码解析

1.Handler的由来 当程序第一次启动的时候,Android会同时启动一条主线程( Main Thread)来负责处理与UI相关的事件,我们叫做UI线程. Android的UI操作并不是线程安全的(出于性能优化考虑),意味着如果多个线程并发操作UI线程,可能导致线程安全问题. 为了解决Android应用多线程问题-Android平台只允许UI线程修改Activity里的UI组建,就会导致新启动的线程无法改变界面组建的属性值. 简单的说:当主线程队列处理一个消息超过5秒,android 就会抛

Android异步消息处理机制(1)Handler基本使用

Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃.解决的方案应该是创建一个Message对象,然后借助Handler发送出去,之后在Handler的handleMessage()方法中获得刚才发送的Message对象,然后在这里进行UI操作就不会再出现崩溃了. 这种处理方式被称为异步消息处理线程.简单的说就是在子线程中实现更新UI操作. Handler基本使用 关于Handler的使用包括两种: 从子线程(worker线程)中向主线程(UI线程)发送消息,在

Android异步消息处理机制(4)AsyncTask源码解析

上一章我们学习了抽象类AsyncTask的基本使用(地址:http://blog.csdn.net/wangyongge85/article/details/47988569),下面我将以问答的方法分析AsyncTask源码内容,源码版本为:API22. 1. 为什么必须在UI线程实例化我们的AsyncTask,并且必须在主线程中调用execute(Params... params)? 在分析为什么在UI线程调用之前,我们先看一下实例化AsyncTask并调用execute(Params...

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

android的消息处理有三个核心类:Looper,Handler和Message.其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道,因此我没将其作为核心类.下面一一介绍: 线程的魔法师 Looper Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程.所谓Looper线程就是循环工作的线程.在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任