深入理解Android异步消息处理机制
2015-12-27 16:00:33 | 来源:玩转帮会 | 投稿:佚名 | 编辑:小柯

原标题:深入理解Android异步消息处理机制

一、概述

Android 中的异步消息处理主要分为四个部分组成,Message、Hndler、MessageQueue 和 Looper。其关系如下图所示:

1. Message 是线程之间传递的消息,它可以在内部携带少量信息,用于在不同线程之间交换数据。

2. MessageQueue 是消息队列,它主要用于存放所有由 Handler 发送过来的消息,这部分消息会一直在消息队列中,等待被处理。每个线程中只会有一个 MessageQueue 对象。

3. Handler 是处理者,它主要用于发送和处理消息。 发送消息一般使用 handler 的 sendMessage()方法,处理消息会调用 handleMessage() 方法。

4. Looper 是每个线程中 MessageQueue 的管家, 调用 loop() 方法后,就会进入到一个无限循环当中,然后每当发现 MessageQueue 中存在一条消息,就会将其取出,并传递到 handleMessage()方法当中。每个线程中也只会有一个Looper对象。

二、详细介绍1、Looper

对于Looper主要是prepare()和loop()两个方法。

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

sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。Looper 就是存储在sThreadLocal里面。这个方法被调用后,首先会判断当前线程里面有没有 Looper对象,如果没有就会创建一个 Looper 对象,如果存在则会抛出异常。可见,prepare()方法,不能被调用两次。这就保证了一个线程只有一个Looper对象。

接下来我们看一下Looper的构造函数:

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mRun = true;
        mThread = Thread.currentThread();
}

在 Looper 的构造函数中,创建了 MessageQueue 对象,这也保证了一个线程只有一个 MessageQueue 对象。

然后我们看看 loop() 方法:

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;
        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        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;
            }
            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
            msg.target.dispatchMessage(msg);
            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }
            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }
            msg.recycle();
        }
}

这个方法先调用 myLooper() 方法,得到 sThreadLocal 中保存的 Looper 对象,并得到 looper 对象对应的 MessageQueue 对象,然后就进入无限循环。

该循环主要包括:取出一条消息,如果没有消息则阻塞; 调用 msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理。

Looper主要作用:

1、 与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
2、 loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。

2、Handler

在使用Handler之前,我们都是初始化一个实例,比如用于更新UI线程,我们会在声明的时候直接初始化,或者在onCreate中初始化Handler实例。

private Handler mHandler = new Handler()
    {
        public void handleMessage(android.os.Message msg)
        {
            switch (msg.what)
            {
            case value:
                break;
            default:
                break;
            }
        };
    };

三、小结

1、首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。大家可能还会问,那么在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler可以成功创建呢,这是因为在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法

2、Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。

3、Handler的构造方法,会首先得到当前线程中保存的Looper实例,并与Looper实例中的MessageQueue相关联。

4、Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。

5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。

tags:

上一篇  下一篇

相关:

10个经典的C语言小程序

今天给大家分享10个比较基础的C语言的小程序,希望给C语言初学者带来一定帮助。1、题目:有1、2、3、4个数字

最通俗易懂的Swift函数式编程

函数式编程(Functional Programming)是相对于我们常用的面向对象和面向过程编程的另外一种开发思维方式,它

手机界无所不在的大猩猩玻璃,要用到汽车上了

新一代福特 GT 超跑将使用康宁大猩猩玻璃作为前风挡和后风挡材料,对,就是智能手机厂商经常挂在嘴边的材料

这对兄弟买买买出了第一家跨国广告集团|这个人有好奇心

他是谁:查尔斯&middot;萨奇和弟弟莫里斯&middot;萨奇(Charles Saatchi, 1943 &#8211; ,Maurice Saatch

分享一组半永久行业投入产出比的数据

现在针对我们做的比较好的行业,半永久(纹绣),分享一组数据吧!本月投入广告费12793.7元,新增加微信521

欧阳妮妮减肥成功 挺妹直播洩「超瘦尖下巴」

欧阳妮妮脸蛋比一个月前(右)消风不少。(取自欧阳妮妮脸书、资料照) 妮妮瘦了。(取自欧阳妮妮脸书) 欧阳娜

想要看着瘦,减几斤才够?

10 秒看全文 1 看脸的世界,从脸上就能出来的瘦,你得减掉 N kg! N(kg)= 1.33 &times; 身高(m) &times;

如何用C语言画一个“圣诞树”?

我使用了左右镜像的Sierpinski triangle,每层减去上方一小块,再用符号点缀。可生成不同层数的「圣诞树」,

使用缓存的9大误区

如果说要对一个站点或者应用程序经常优化,可以说缓存的使用是最快也是效果最明显的方式。一般而言,我们会

Windows下搭建ReactNativeAndroid开发环境详解

最近看到React Native好像好厉害的样子,好奇心驱使之下体验了一下并将在Window下搭建React Natvie Android

站长推荐: