您的位置:首页 > 移动开发 > Android开发

Android消息机制浅析——原理探究

2016-04-05 13:55 543 查看

Android消息机制浅析——原理探究

在博客Android消息机制浅析——基本使用 中,我们介绍了Android消息机制的基本使用以及一些常用方法的使用。本篇,我们就探究下源码。

系列地址:

Android消息机制浅析——基本使用

Android消息机制浅析——原理探究

Android消息机制浅析——面试总结

一、概述

Android消息机制浅析——基本使用一文中,我们对Android消息机制中的Handler和Message有了一个基本的了解,能够进行线程之间的通信。如果我们稍加注意,会注意到Hanlder的多个构造函数中会提到一个Looper的类。所以,在这次源码探究中,我们会涉及到Looper、Handler、Message、MessageQueue几个概念。

Message:消息体对象,可用于存储我们要传送的数据。

MessageQueue:消息体队列,用于管理Handler发送的Message对象。

Looper:循环控制器,用于控制我们的消息管理,消息的取出与使用。

Handler:消息的处理器,用于消息的发送与处理。

它们各司其职,就完成了Android整个消息的流通。

二、源码探究

1、Message对象

Message在整个Android的消息机制中充当着消息体的角色,它用于包装存储我们的数据在消息之间进行传递。如何创建一个Message对象呢?

/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
*/
public Message() {
}


我们可以看到它有一个无参的构造函数,但是系统并不建议我们使用它的构造函数来创建Message对象,而是建议我们使用obtain()方法来创建Message对象,显然这个方法肯定是个静态方法。

public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}


先判断sPool这个Message对象是否为空,如果不为空,就将这个Message对象进行复用,同时对这个Message的标识信息进行初始化,去除标记,然后将sPoolSize减1.如果为null,则通过构造函数进行创建一个新对象。通过对比构造函数,我们发现通过obtain方法进行创建对象,提供了效率,减少了不必要的Message对象创建。

我们在Android消息机制浅析——基本使用中简单介绍了Message对象,我们知道它有许多成员变量用于帮助我们存储数据。我们从源码中简单的摘要出来。

public final class Message implements Parcelable {
/**
* 用户定义的区别标识
* with other handlers.
*/
public int what;

/**
* 用于存储一些简单的int值
*/
public int arg1;
public int arg2;
//存储Object对象
public Object obj;
//存储Bundle数据
Bundle data;
//存储Hanlder对象,与发送它的Hanlder进行绑定关系
Handler target;
//Message的标识,正在使用
static final int FLAG_IN_USE = 1 << 0;
static final int FLAG_ASYNCHRONOUS = 1 << 1;
static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
//Message的标识
int flags;
//有时为了保存一些信息,用于设置next
Message next;
//存储Runnable
Runnable callback;
.....
}


在源码中我们同样可以发现obtain()方法的几个重载函数。

public static Message obtain(Message orig)

public static Message obtain(Handler h)

public static Message obtain(Handler h, Runnable callback)

public static Message obtain(Handler h, int what)

public static Message obtain(Handler h, int what, int arg1, int arg2)

public static Message obtain(Handler h, int what,

int arg1, int arg2, Object obj)

在这几个重载函数中,我们可以在obtain函数进行一些变量的初始化,完成Message的创建。我们从中摘取public static Message obtain(Handler h, int what,

int arg1, int arg2, Object obj) 给大家示例。

public static Message obtain(Handler h, int what,
int arg1, int arg2, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
m.obj = obj;

return m;
}


在这个构造方法中,我们要着重注意一点:Message与Hanlder的绑定,Message通过target存储handler就行关系的绑定。

当一个消息体完成它的使命后,它就该被销毁回收,所以这里Message通过recycle()方法用于销毁Message对象。

public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}


在Message进行回收的时候,首先判断该Message对象是否正在使用,如果正在使用,判断gCheckRecycle变量是否为真如果为true,则抛出异常并返回,为false则直接返回。补充下gCheckRecycle这个成员变量。

private static boolean gCheckRecycle = true;


这个对象初始值为true,通过方法updateCheckRecycle进行更改:

public static void updateCheckRecycle(int targetSdkVersion) {
if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
gCheckRecycle = false;
}
}


如果系统版本小于21,则为false。(深入原因估计是系统源码又改了)。

当一个Message不处于正在使用的时候,系统通过recycleUnchecked()方法进行回收。

/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;

synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}


回收所做的工作就是清除一个Message对象的细节属性,置空初始化。

小结:

Message作为消息体在消息机制中存在,系统建议我们使用Message.obtain()方法进行创建一个Message对象,目的是为了提高使用率。同时对于一个正在使用中的Message对象是无法进行回收的,系统报出异常“This message cannot be recycled because it is still in use.”

2、MessageQueue对象

消息队列,在消息机制中充当仓库的作用,用于存储Message对象。它持有的Message对象通过Looper进行分发,同时Message对象不是直接通过MessageQueue对象的方法进行添加,而是通过Handler进行添加。

既然作为一个消息体的“仓库”,我们只重点关心它的添加、删除、遍历即可。

boolean enqueueMessage(Message msg, long when):添加Message

void removeMessages():删除指定的message

Message next():遍历Message对象

(1)、boolean enqueueMessage(Message msg, long when)

用于添加我们的Message。

boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}

synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
msg.recycle();
return false;
}

msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue.  Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
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;
}

// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}


enqueueMessage的整体流程:

首先判断Message对象的target是否为null(所附属的Handler,说白了就是发送源是否为null),如果为null,抛出异常“Message must have a target”。接着判断该Message是否正在使用,如果是,抛出异常“ This message is already in use.”。当上面条件不符合,则该Message是合法的,才开始添加到MessageQueue中。接着判断mQuitting是否为true,如果为true,抛出异常“sending message to a Handler on a dead thread”,说明依附的Hanlder所在的线程已死。所有的检查都不符合,则开始进行添加,首先通过markInUse()方法设置Message的flags |= FLAG_IN_USE;接着设置when参数,最后插入到链表中。下面我们就开始分析这个Message的插入流程顺序:



(2)、void removeMessages()

用于删除我们指定的Message。它有两个重载函数:

void removeMessages(Handler h, int what, Object object)

void removeMessages(Handler h, Runnable r, Object object)

原理都相同,挑一个研究下。

void removeMessages(Handler h, int what, Object object) {
if (h == null) {
return;
}

synchronized (this) {
Message p = mMessages;

// Remove all messages at front.
while (p != null && p.target == h && p.what == what
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}

// Remove all messages after front.
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.what == what
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}


源码也不难,主体流程就是根绝链表节点遍历Message对象,然后比较我们指定的what、object,如果相同则删除,继续遍历下去,直到结束。

(3)、Message next()

Message next() {
....
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());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready.  Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (false) Log.v("MessageQueue", "Returning message: " + msg);
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
...
}


针对next()方法,我只挑出了里面的遍历部分,同样的逻辑根据链表头进行MessageQueue的遍历,返回msg。

3、Looper对象

我想把Looper对象比作“电机”、“发动机”一点不为过。我们知道Thread默认情况下是不具备消息循环的。为了使它们具备,我们需要在线程中调用prepare()方法,同时通过loop()方法获取Message。 例如:

class LooperThread extends Thread {
public Handler mHandler;
public void run() {
//将当前线程初始化loop线程
Looper.prepare();

mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
//开始循环消息队列
Looper.loop();
}
}


下面就开始对Looper源码的探究吧!首先看下常见的方法

prepare()

loop()

myLooper()

myQueue()

(1)、Looper.prepare()

这个方法用于将我们的线程转换为loop线程,它做了哪些工作?

public final class Looper {
private static final String TAG = "Looper";

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

final MessageQueue mQueue;
final Thread mThread;

private Printer mLogging;

/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
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));
}
//其它
}


我们可以看到,prepare方法就是给当前Thread设置Looper对象,同时也可以知道每个Thread只能有一个Looper对象,核心就是讲线程依附的Looper对象转换为ThreadLocal类型。

(2)、Looper.loop()

在loop方法里,进行Message的遍历获取。

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

public static Looper myLooper() {
return sThreadLocal.get();
}


首先通过myLooper()方法获取Looper对象,如果Looper对象为null,则抛出异常“No Looper; Looper.prepare() wasn’t called on this thread.”。获取该Looper对象中的MessageQueue对象,进行遍历获取Message对象,同时将取出的Message进行销毁。这里我们着重注意msg.target.dispatchMessage(msg);这个方法就是调用我们的Handler的dispatchMessage方法,然后进行消息的处理。

(2)、MessageQueue myQueue()

获取Looper对象中的MessageQueue。

public static MessageQueue myQueue() {
return myLooper().mQueue;
}


小结:

Looper中包含一个MessageQueue用于存储我们的Message消息队列。

每个线程只能有一个Looper对象,且该Looper对象为ThreadLocal类型

通过Looper.prepare()方法可以将线程变成Looper线程。

4、Handler对象

我们在上篇文章中也简要的领略了Handler的使用,它提供了多个方法供我们发送消息,同时进行消息的处理。它有多个重载构造方法。

public Handler()

public Handler(Callback callback)

public Handler(Looper looper)

public Handler(Looper looper, Callback callback)

public Handler(boolean async)

public Handler(Callback callback, boolean async)

public Handler(Looper looper, Callback callback, boolean async)

同时Handler内部对应着以下几个成员变量:

final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;


它里面包含一个Looper对象、MessageQueue对象。Handler的构造方法最后都归结到两个构造方法上。

public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}

mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}


在Handler中通过

mLooper = looper;
mQueue = looper.mQueue;


建立Looper、MessageQueue之间的关系。

我们看下Hanlder的消息发送、消息处理的方法。

sendXXXXMessage()

postXXXX()

handleMessage(Message msg):消息处理

(1)、sendXXXXMessage()

用于发送Message消息序列,它们最后都是调用sendMessageAtTime()方法。

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

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


将消息插入队列,同时在enqueueMessage方法进行Handler与msg的绑定。我们发送的消息,需要我们在handleMessage(Message msg)方法中进行处理:

public interface Callback {
public boolean handleMessage(Message msg);
}

/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}


这是一个空方法,子类需要重写它进行业务逻辑的处理。这里我们就需要注意构造函数中的Callback接口,这个接口就是封装了我们的handleMessage,用于我们的消息回调。

(2)、postXXXX()

用于发送Runnable的线程处理,可以在Runnable线程中直接操作UI。

public final boolean post(Runnable r)
{
return  sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}


我们可以看到在post方法中,同样是调用sendMessageXX系列,同时通过getPostMessage()方法获取一个Message对应,该Meesage的callback接口绑定到Runnable上。后面就是Looper进行变通过dispatchMessage()方法,执行Runnable中的内容。这里补充一下dispatchMessage方法的介绍:

public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}


这个方法中首先判断msg.callback是否为null,这是针对我们使用post发送消息处理的,然后在进行mCallback是否为null,这是针对我们的sendXXMessage进行处理的。

至此,总体的流程中的各个部分,我们已经梳理完毕。下面就总结一下,借用引用CodingMyWorld的图:



我就依照此图总结下:

1、首先创建Message对象,Message对象用Hanlder target字段存储Hanlder,实现该Message与发送源Handler的关系绑定,因为一个Handler可以发送多个消息,同样一个Message也可以被多个Handler发送,确保最后调用正确的Handler来处理该消息的回调。用Runnable callback存储Runnable。

2、创建Handler时,会指定Handler内部的Looper成员变量,同时制定成员变量mQueue = looper.mQueue;

3、Handler通过sendXXMessage()方法发送消息,该系列方法的本质是调用sendMessageAtTime方法,该方法显示判断MessageQueue是否为null,然后调用enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)实现Message与Handler的绑定(msg.target = this),在调用MessageQueue的插入队列方法,形成MessageQueue消息组。该消息组就是Looper内部的mQueue。

4、通过第3步,Looper内部的MessageQueue就建立了,Looper内部通过loop()方法进行遍历MessageQueue,通过msg.target.dispatchMessage(msg);调用Handler的分发事件实现消息回调的处理。

至此,整个流程就梳理通了。同时推荐大家阅读下面的参考文章,很不错。

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

=================================================

作者:邓士伟

博客:http://blog.csdn.net/mr_dsw

理念:转载注明出处,进步来源于分分享

=================================================
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: