聊聊Android的消息机制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/KING_GUOGUO/article/details/82960604

Android的消息机制简单点说就是Handler的运行机制和它所附带的MessageQueue和Looper的工作过程。

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

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

Android的消息机制.png

一、为什么提供这种机制

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

那么问题来了…

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

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

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

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

二、MessageQueue(消息队列)

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

扫描二维码关注公众号,回复: 3502289 查看本文章

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

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

//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,所以它就能够将消息切换到指定的线程中执行。

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上。

猜你喜欢

转载自blog.csdn.net/KING_GUOGUO/article/details/82960604