Android 消息机制 ,Handler、Message、Looper源码分析,示例源码

时间:2021-01-13 05:36:47

1、Android消息机制

在Android系统中,线程内部或者线程之间的数据交互通常需要使用到消息,比如刷新页面操作需要在UI线程(也就是Android主线程)中执行,而有些数据操作(比如数据库操作、网络数据请求或者其他耗时操作)需要在子线程中执行,此时就需要有一个有效的数据传递方法来实现它。Android系统为此提供了一套消息机制来实现主线程和子线程之间的通信。

另外需要说明一点,Android消息通信机制并非只能在子线程和主线程之间通信,任意两个子线程之间也是可以通过消息传递机制进行通信的。这个后面会通过结合例子说明这两种通信方式的使用和差别。
下图就是消息机制的一个简单框图。

Android 消息机制 ,Handler、Message、Looper源码分析,示例源码


从上图可以看出,消息机制需要的几个要素如下:

1、消息对象:Message

消息对象,顾名思义就是记录消息信息的类。这个类有几个比较重要的字段:

a、arg1和arg2:我们可以使用两个字段用来存放我们需要传递的整型值。

b、obj:该字段是Object类型,可以传递自定义的对象信息。

c、what:这个字段可以说是消息的标志,在消息处理中,我们可以根据这个字段的不同的值进行不同的处理。


2、消息队列:MessageQueue

用来存放Message对象的数据结构,按照“先进先出”的原则存放消息。存放并非实际意义的保存,而是将Message

对象以链表的方式串联起来的。MessageQueue对象不需要我们自己创建,而是有Looper对象对其进行管理,一个线

程最多只可以拥有一个MessageQueue。我们可以通过Looper.myQueue()获取当前线程中的MessageQueue。


3、消息处理:Handler

包括将消息传递到消息队列和消息循环从消息队列中取出后对消息的处理。


4、消息循环:Looper

主要用于循环读取消息队列中的消息,并且将获取到的消息传送给Handler处理。

具体的消息处理流程如下:

1、通过Handler将消息传递到MessageQueue消息队列中,此处的Handler可以在不同的线程中进行传递,最后都集中到同一个消息队列中。比如你在主线程中创建了一个Handler对象,此时你可以通过这个对象在不同的子线程中发送Message对象,消息对象均加入到主线程的消息队列中。

2、每个Looper对应一个MessageQueue消息队列的管理,Looper会不停的的对队列进行读取,如果消息队列有数据,则会将消息传递到Handler所在线程进行处理。

3、Handler通过handleMessage进行接收数据并对接收到的消息进行处理。

以上是针对消息内部处理流程的一个简单说明,下面会通过相关源码进行分析。


2、Handler源码分析

Handler传递消息的方法有两种:

1、使用handler.sendMessage(Message m);

2、使用handler.post(Runnable r);


以下先从sendMessage方法分析

Handler针对立即发送或者延迟发送处理以及Message的不同提供的不同的方法。

sendMessage(Message msg);

sendEmptyMessage(int what);

sendMessageDelayed(Message msg,long delayMills);

sendEmptyMessageDelayed(int what,long delayMills);

sendMessageDelayedAtTime(Message msg,long uptimeMills);

sendEmptyMessageDelayedAtTime(int what,long uptimeMills);


/**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}

/**
* Sends a Message containing only the what value.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}

/**
* Sends a Message containing only the what value, to be delivered
* after the specified amount of time elapses.
* @see #sendMessageDelayed(android.os.Message, long)
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}

/**
* Sends a Message containing only the what value, to be delivered
* at a specific time.
* @see #sendMessageAtTime(android.os.Message, long)
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/

public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}

/**
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}

从以上源码可以看出,其实所有方法归根到底都是调用同一个接口sendMessageAtime(Message msg,long uptimeMills),
从而统一执行将消息推入到消息队列中,代码如下:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

以下针对post(Runnable r)方法分析

post方法与sendMessage方法类似,也针对立即发送或者延迟发送处理以及Message的不同提供的不同的方法。

此处就不再全部贴出说明,只用其中一个进行分析。

public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
从以上源码发现,其实也是调用了sendMessage中的相关方法。

参数中getPostMessage(r)源码如下所示:

   private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
getPostMessage(Runnable r)将Runnable接口封装成了一个消息,r作为消息的回调处理。

结合以上Handler的两种方法得出:Handler在send和post方法中,传递流程基本相同,唯一的差别就是,从Looper返回给Handler处理的时候方法不一致,具体源码如下图所示:

  //此方法用于Looper传递过来的消息分发
    public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }
  //此方法用于处理post传递消息接收处理。
 private static void handleCallback(Message message) {        message.callback.run();    }
  //此方法用于send传递消息的接收和处理。    public void handleMessage(Message msg) {    }

总结两种方法的差异就是:

post方法的消息处理是在Runnable接口的run方法中执行;

send方法的消息处理是在handleMessage方法中进行执行。



3、Looper源码分析

MessageQueue的管理者,在一个线程中,如果存在Looper对象,则必定存在MessageQueue对象,并且只存在

一个Looper对象和一个MessageQueue对象。在Android系统中,除了主线程有默认的Looper对象,其它线程默认是没

有Looper对象。如果想让我们新创建的线程拥有Looper对象时,我们首先应调用Looper.prepare()方法,然后再调用

Looper.loop()方法。

 public static void prepare() {
prepare(true);
}

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));
}

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


以上是Looper初始化,包括初始化消息队列和当前的线程对象,并通过sThreadLocal保存在当前的线程本地变量中。

顺便说下,

ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。


loop()方法就是获取当前线程中的looper对象,并且不断读取消息队列中的消息,进行分发处理。也就是说消息机制的推动者就是Looper,消息机制的发起者和处理就是Handler。


 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.recycleUnchecked();
}
}