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

Android线程间消息机制-Handler源码分析(FrameWork)

2017-10-27 10:47 573 查看
本文基于android 7.1.1的源代码,来对Handler的消息机制进行分析

一、概述

Android线程间的消息机制最常用的就是用Handler来实现,而Handler的实现还需要借助Looper、MessageQueue、Message这三个类,下图为这4个类之间的关系

1.1 UML类图关系



消息的机制主要包含

* Handler *:主要功能是向消息池发送消息(
sendMessage
)和处理相应的消息(
handleMessage
);

* Message *:主要功能是作为消息的载体,包括软件消息和硬件消息;

* MessageQueue *:主要功能是负责将消息储存到消息池里(
enqueueMessage
)和从消息池取出消息(
next
);

* Looper *:主要功能是不断的循环执行(
loop
),按分发机制将消息分发给目标处理者;

在正文开始前,再来普及一些概念

* Process * : 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。进程之间内存是不同享的。

* Thread * :线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。同一进程下,线程共享内存。

1.2 Demo

public class MainActivity extends AppCompatActivity {

private static Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
//自定义处理
Log.i("TAG","收到的消息:"+msg.what);
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
message.what = 1;
mHandler.sendMessage(message);
}
}).start();
}
}


上面这段代码应该是最简单的Handler使用例子了。这段代码里面只涉及到了Handler以及Message,并没有看到Looper以及MessageQueue。我再给段伪代码让大家好理解。

1.3.1 伪代码

public class ActivityThread extends Thread{
private Handler mHandler;

@Override
public void run() {

Looper.prepare(); //【见2.1】

ActivityThread thread = new ActivityThrad();

//建立Binder通道(创建新线程)
thread.attach(false);

mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
//TODO 自定义处理消息【见3.2】
}
};

Looper.loop(); 【见2.2】
}
}


上面的代码大致上就相当于我们demo给出的例子的Looper缺失部分。一般如果我们在Activity创建前就会把Looper实例好,所以,我们日常使用Handler时,都没有发觉到Looper的存在。MessageQueue这类我们放在后面再来跟大家讲。我们先来看下这个段伪代码做了哪些操作。

二、Looper

2.1 prepare()

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

//quitAllowed 为true的话,在looper运行退出,为false的话,则是Looper不运行退出
private static void prepare(boolean quitAllowed) {
//每个线程只允许执行一次该方法,第二次执行时线程的TLS已有数据,会抛出异常。
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//给线程的TLS赋值
sThreadLocal.set(new Looper(quitAllowed));
}


这里的
sThreadLocal
其实是ThreadLocal类型

* ThreadLocal * 线程本地存储区(Thread Local Storage ,简称TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。 下面来看下ThreadLocal的
set
方法和
get
方法。

public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程的TLS
ThreadLocalMap map = getMap(t);
if (map != null)
//重新赋值
map.set(this, value);
else
//保存value到当前线程的this
createMap(t, value);
}


public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程的TLS
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
//将存储的值返回
return (T)e.value;
}
//不存在这个值,则重新实例ThreadLocalMap,并将this作为key,value为null
return setInitialValue();
}


这两段代码都涉及ThreadLocalMap这个类,其实这个类是ThreadLocal的内部类。而这个类里面又有内部类Entry,Entry就是作为存储的类型。

ThreadLocal的get()和set()方法操作的类型都是泛型,接着回到前面提到的sThreadLocal变量,其定义如下:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>()


可见sThreadLocal的get()和set()操作的类型都是Looper类型。

我们现在再回到上面的prepare的方法,在调用set方法传入的是Looper的构造方法。

private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);  //创建MessageQueue对象. 【见4.1】
mThread = Thread.currentThread();  //记录当前线程.
}


Looper.prepare()方法到此结束,到这里我们可以了解到,Looper到这里就跟MessageQueue以及Thread建立关系。

2.2 loop

/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
//获取当前线程的looper实例
final Looper me = myLooper();
//调用loop前一定要先调用prepare()方法,否则会报错
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//将当前线程的Looper中的消息队列MessageQueue
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 (;;) {//进入loope的主循环
//从消息队列取消息
Message msg = queue.next(); // 可能会阻塞 【见4.1】
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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}

final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
//调用Message自带的Handler的dispatchMessage()进行分发消息
msg.target.dispatchMessage(msg);//【见3.1】
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}

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.
// 确保分发过程中identity不会损坏
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);
}
//将Message放入消息池
msg.recycleUnchecked();
}
}


到这里主线程就进行
死循环
,这个循环就是不停的取消息。也就是说当一个Activity实例好,Looper这块就已经是处于取消息阶段,只要有消息进来,立马触发next取出消息。而且这里的消息不单单局限于我们自己创建的Handler发送过来的消息。其实整个Activity的生命周期也是通过Handler的消息机制维护的,具体的可以去学下Activity的启动源码。这里有个问题,就是这个死循环运行在UI线程却不会导致ANR的发生。是因为* Android应用程序的主线程在进入消息循环过程前,会在内部创建一个Linux管道,这个管道的作用就是使得主线程在消息队列为空时可以进入空闲状态,当有消息来时,再唤醒主线程进行工作。 所以,也可以这么说Handler的底层机制其实是 Linux的管道技术 *,正是由于这个技术,让发送消息以及处理消息能够运行于在不同的线程上。

当取到消息时,就会调用Message的Handler成员的
dispatchMessage
方法进行分发消息。

三、Handler

我们先从Handler的构造方法进行分析

// 一般情况我们都是调用无参的构造方法
public Handler(Callback callback, boolean async) {
//匿名类、内部类或本地类都必须申明为static,否则会警告可能出现内存泄露
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());
}
}
//获取当前线程的Looper对象
//必须先执行Looper.prerare(),对能获取Looper对象,否则会报错
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//获取Looper的消息队列
mQueue = mLooper.mQueue;
//回调方法
mCallback = callback;
//设置消息是否为异步方式
mAsynchronous = async;
}


这里可以看出,Handler的MessageQueue由Looper决定,并且Handler所在的线程也是由Looper决定的。

3.1 dispatchMessage

/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
//msg的callback不为空,就回调msg的callback方法
if (msg.callback != null) {
handleCallback(msg);
} else {
//handler本身的mCallback不为空,也即是有参构造函数里面传入
//回调mCallbackd的handleMessage方法
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//前两者不满足情况下,调用handleMessage方法
handleMessage(msg);
}
}


从上面分发机制可以看出,Message本身的回调优先级是最高的,而Handler本身的方法级别是最低的。所以按照优先级从高到低的排序:

1、Message的回调方法:
msg.callback.run()


2、Handler的回调方法:
Handler.mCallback.handleMessage(msg)


3、Handler的默认方法:
Handler.handleMessage(msg)


我们平常使用最多的方法其实是Handler自身的HandleMessage方法,到这里我们已经大致了解到从消息队列里面取到消息,并且将消息分发到相应的Handler进行处理。这里已经讲完消息机制的后半段,接着来分析消息机制的前半段-发送消息到消息队列。

public final boolean sendMessage(Message msg)
{
//延时为0
return sendMessageDelayed(msg, 0);
}


public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}


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) {
//将当前的Handler赋值给Message
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);【见4.2】
}


这边特意列出这么多个方法,就是为了说明消息的添加是MessageQueue的
enqueueMessage
执行的。我们再来看看Handler其他一些常用的方法,是如何将消息传递过去的。

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


public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}


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


从最后一个方法里面可以看出,利用Handler的post、postDelayed方法,最终消费的是Message的回调方法callback,这个按照3.1的分发机制可以得出。

我们再来看下我们的demo给出的方法又是怎么实现的

public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}


从这里可以看出,post跟send方法的区别在于post方法需要传入
Runnable
,而send的不需要,最终导致消息的消费位置不一样。

四、MessageQueue

4.1 next

Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) { //当消息循环已经退出,则直接返回
return null;
}

int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞操作,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会返回
//native方法
nativePollOnce(ptr, nextPollTimeoutMillis);

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.
//当消息Handler为空时,查询MessageQueue中的下一条异步消息msg,则退出循环。
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 (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}

// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}

// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
//当消息队列为空,或者是消息队列的第一个消息时
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run.  Loop and wait some more.
//没有idle handlers 需要运行,则循环并等待。
mBlocked = true;
continue;
}

if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}

// Run the idle handlers.
// We only ever reach this code block during the first iteration.
//只有第一次循环时,会运行idle handlers,执行完成后,重置pendingIdleHandlerCount为0.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
//去掉handler的引用
mPendingIdleHandlers[i] = null; // release the reference to the handler

boolean keep = false;
try {
keep = idler.queueIdle(); //idle时执行的方法
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}

if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}

// Reset the idle handler count to 0 so we do not run them again.
//重置idle handler个数为0,以保证不会再次重复运行
pendingIdleHandlerCount = 0;

// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
//当调用一个空闲handler时,一个新message能够被分发,因此无需等待可以直接查询pending message.
nextPollTimeoutMillis = 0;
}
}


上面的代码主要的工作就是取出消息,但是真正的工作是在Native层实现的,调用nativePollOnce方法,实现与Native层的通信。这里我们不对Native层的实现进行深究,有兴趣的可自行查阅资料了解。后期我也会写关于Native层的实现机制。

4.2 enqueueMessage

boolean enqueueMessage(Message msg, long when) {
//Message的target必须不能为空
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) {//正在退出时,回收msg,加入消息池
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle(); 【见5.2】
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.
// p为null(代表MessageQueue没有消息)或者msg的触发时间是队列中最早的
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.
//将消息按时间顺序插入到MessageQueue。正常情况不需要唤醒,除非消息队列存在barrier,并且同时Message是队列中最早的异步消息
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.
//消息没有退出,我们认为此时mPtr != 0
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}


看到这里,我们可以了解到消息的产生以及添加到队列中去。回到上面写的Demo,
handler.sendMessage
到这里就结束了,也就是说这个新建的线程到此就没了。接下来就要唤醒主线程了,这个唤醒主要是唤醒【4.1】提及到的
nativePollOnce
方法;当有消息入队之后,nativePollOnce方法就会被调用,然后就开始分发消息了。那么整个Handler的消息机制就走完了。

## 五、Message

### 5.1 obtain

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
//消息池数量减1
sPoolSize--;
return m;
}
}
return new Message();
}


这个方法是很多人都推荐调用的,而不是调试Message的构造方法。因为这个是重复利用消息池的空闲消息,提高性能。

### 5.2 recycle

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


### 5.3 recycleUnchecked

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) {
//将没用的Message回收到消息池里
next = sPool;
sPool = this;
sPoolSize++;
}
}


## 总结

本文从一开始构建循环体Looper、从MessageQueue取出消息到Handler消费消费的过程,然后再对Handler发送消息,紧接着消息入队列这一过程的分析。

Looper在初始化的时候会跟当前所在的线程绑定,并且实例化消息队列MessageQueue。而Handler在实例化的时候,如果没有指定looper的话,默认调用会从当前线程的TLS中取出Looper。所以实例Handler之前,必定是调用了Looper.prepare的。并且结合管道技术,实现一个线程负责取消息,另外一个线程负责添加消息。实现了线程间的通信。

还是用一张图来总结整个消息机制吧。



1、Handler的sendMessage将Message送到MessageQueue中,此时触发管道(Pipe)唤醒另外一端开始读数据

2、MessageQueue的next返回Message,并由Message的Handler成员调用dispatchMessage将Message进行处理(消费)

3、Looper在调用loop后就一直处于等待状态,一直到有Message到来,并将其唤醒,然后再将Message发出去
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 源码