聊聊Android的消息机制

Android的消息机制主要是指Handler的运行机制和它所附带的MessageQueue和Looper的工作过程。

Handler、MessageQueue和Looper这三者其实是一个整体。但是我们要想清楚这个整体的工作流程就需要逐个击破。

下图罗列了这次分享说的重点

Android的消息机制.png

一、为什么提供这种机制

系统之所以提供这种机制主要是为了解决在子线程不能访问UI的矛盾。

那么问题来了......

1.为什么子线程不能访问ui呢?

因为Android的ui控件不是线程安全的。如果在多线程的情况下并发访问就会导致ui控件处于不可预期的状态。

2.那么在这种情况下为什么不对ui控件的访问加上锁机制呢?

首先加上锁的机制会使访问ui控件的逻辑变得复杂,其次因为锁机制会阻塞某些线程的进行,从而降低UI访问的效率。基于这两个原因最好的办法就是使用单线程处理UI控件。反之对于开发者来说只需要利用Handler切换一下线程就可以了,也不是很麻烦

二、MessageQueue(消息队列)

Android的消息队列是MessageQueue,它主要有两个功能:插入和读取。

它的内部实现是并不如它名字那样是个队列,而是用一个单链表来维护消息列表,我们知道单链表的数据结构实现插入和删除的效率高。

插入的操作很简单,本质就是单链表的插入操作。

//MessageQueue的插入操作  -enqueueMessage方法
  boolean enqueueMessage(Message msg, long when) {

     ......
     synchronized (this) {
     ......

     for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
     }
     ......

}

读取的操作next方法,通过无限的循环来检查有没有新消息,当有新消息来的时候,next方法会返回这条消息并将其从单链表中移除。

//MessageQueue的读取操作(伴随着删除操作)-next方法

  Message next() {
        ......
 synchronized (this) {
        ......
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
        .....
          }

三、ThreadLocal的工作原理

ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据和读取数据。

它的特点通俗点说就是,不同的线程访问同一个对象,它们通过ThreadLocal获得的值是不一样的。

还可以这么神奇?

首先它是一个泛型类

public class ThreadLocal<T> {}

其次通过set方法我们可以看到它使用了Map的数据结构,它的key就是线程,value就是该对象的值。

//获取到当前线程的该变量的值,如果没有则初始化一个。

 public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

set方法将线程对应的值进行存储
之后,通过get方法就可以获取到该对象在这个线程中的值。


public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    

最后抛出一个问题

为什么通过ThreadLocal可以在不同的线程中维护一套数据的副本并且互不干扰?

因为不同的线程访问同一个ThreadLocal的get方法,ThreadLocal内部会从各自的线程中取出一个数组,然后再从数组中根据当前的ThreadLocal的索引去查找对应的value值,这样不同的线程中取出的数组自然是不同的。简单的说就是它们所对ThreadLocal的读写操作仅限于各自的线程内部。

四、Looper的工作原理

我们知道,Handler的工作需要Looper,没有Looper的线程就会报错。

那么Looper是做什么的呢?

具体来说,就是不停的从MessageQueue中查看是否有新的消息,如果有消息就会立刻处理,否则就一直阻塞在那里等待新的消息。

如何为线程创建Looper?

很简单,通过Looper.prepare就可以为线程创建一个Looper,这样这个线程就有了属于他的Looper。之后调用Looper.loop()就可以开启消息循环。

Looper最重要的就是loop方法,它的工作原理也很简单,首先它是一个死循环,会调用MessageQueue的next方法不断的获取新的消息并处理,没有消息的时候就会阻塞在那里。直到Looper调用quit或者quitSafely方法。也就是说我们最后是要在合适的时候退出Looper的。否则就会一直循环下去。

五、Handler的工作原理

Handler的工作主要包含消息的发送和接收。

1.发送消息主要通过post和send的一系列方法实现
mHandler.postDelayed(runnable,0);

发送消息的过程只是向MessageQueue中插入一条消息,它的next方法就会把这条消息返回给Looper,Looper收到消息后就开始处理了。最终消息由Looper交由Handler处理。由于Looper里面使用了ThreadLocal<Looper>,所以它就能够将消息切换到指定的线程中执行。

2.处理消息

Looper将消息交由Handler处理,就会调用Handler的disaptchMessage方法。这时候就进入了处理消息的阶段。

 /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }


首先,检查Message的callback是否为null,不为null就通过handleCallback处理消息。这个callback是一个Runnable对象,也就是我们在post方法里传递的Runnable参数。

其次检查mCallBack是否为null,不为空就调用handleMessage方法。CallBack是一种当我们不想通过派生子类创建Handler的另外一种实现方式。

最后调用Handler的handlerMessage来处理消息

到这里我们就聊完了Android的消息机制,它的作用就是很轻松的将某个任务或者说是消息切换到指定的线程(Handler所在的线程)中执行。本质上来说,它并不是专门用于更新UI,只是常常被用在更新UI上。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容