说明:本文是基于Android6.0源码来分析的
- 这片文章主要是从源码的角度来分析Android中的消息机制是如何运行的,并不会介绍如何使用Handler。
- Android的消息机制可以说是Android的血液,流淌在不同的app之间,催动这各种事件有序的执行。
- Android进程在启动的时候会调用ThreadActivity的main方法,从main方法中我们可以看出,Android的app进程启动以后只有一个线程ActivityThread他是main线程,除了创建主线程方法以外,还为我们的主线程准备了mianLooper,最后开启了一个Looper 循环
整个消息机制的一个重要用途,就是线程间通信,而且大部分都是工作线程向主线程发送数据和消息。那么为什么Android系统要这样设计呢?其实典型的Android应用,都是事件驱动的GUI程序,跟Window的GUI程序很类似。这种程序,特点就是基于事件运行,比如点击事件或者滑动事件。所以这种情况下,肯定是要有一个专门的线程来负责事件的监听和分发。在Android中,系统默认启动的主线程,就干这个事情了。
由于该消息分发线程有着自己独特的任务,所以如果该线程阻塞了的话,系统就会出现无响应的情况。这样,自然就不可能把耗时的任务放在该线程中,所以官方推荐是把耗时的任务放到工作线程中执行。但是很多时候,耗时任务的执行结果,都是要反馈到UI上的。而Android中的View,是不能在非UI线程中更新的,因为View不是线程安全的,没有同步,所以必须要把数据通过线程间通信的模式,发送到UI线程,这能才可以正常更新UI。
所以大家会问,Android为什么不把View设计成线程安全的呢?那么在Java这种指令式编程语言中,线程安全就是意味着要加锁。其实我们可以思考一下,整个View响应事件,事件从屏幕产生,经过Framework,最后到kernel,然后kernel处理完成后,向上传递到framework,然后又传递到View层。如下图所示:
那么如果整个流程都是线程安全的话,就会面临着两个相对的加锁流程,这种反方向的加锁,很容易就会导致死锁的情况发生。这是绝大多数GUI系统存在的问题,所以绝大多数GUI系统都是采用事件分发机制来实现的。所以说Android为什么设计成单线程模型了。
public static void main(String[] args) {
...
//为主线程准备一个loop
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
//开启消息循环。
Looper.loop();
...
}
prepareMainLooper方法主要是准备Lopper,里面还掉用了 prepare(false);方法,注意一下参数,传的是false,也就是不允许主线程的loop推出;如果我们在自己创建的线程去创建looper的以后,我们这里传的就是true了。
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//我们自己创建的线程准备looper的时候,会调用这个方法,可以看到传了false。
public static void prepare() {
prepare(true);
}
接下来我们分析一下loop()方法。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;
}
//把消息分发给对应的handler处理。
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
//把已经处理的消息放回到消息池里面,用来复用这个消息。这个消息吃的最大缓存熟是50。
msg.recycleUnchecked();
}
}
那么消息队列是在什么时候创建的呢?其实是在为每个线程准备Lopper的时候创建的,是在Looper的构造函数里
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
从上面的逻辑我们可以看出,每个Looper的创建都会伴随着一个消息队列的产生,也就是不同线程之间的消息队列是不一样的,每个线程都会创建一个消息队列。另外looper里面的一个成员sThreadLocal,这个是ThreadLocal类型的,ThreadLocal这个类会单独为每个线程存储一个本线程单独使用的Looper,线程之间不会共享,也不会相互干扰。
- 消息循环我们就分析完了,接下来我们分析消息是怎么分发到消息队列里的
- 消息的分发是通过下面这样的方式发送出去的。
Handler mHandler = new Handler(Looper.getMainLooper());
Message msg = Message.obtain()
给消息中添加参数的标识。
msg.what = 1;//这个消息的标识
msg.ars1 = 2;//这个消息锁携带的数据
mHandler.sendMessage(msg) //发送消息
可以看出,发送消息需要一个消息载体Message和运送载体的Handler
- 我们来分析一下Handler的构造函数
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
//这里为我们自定义Handler给出来知道方针,不推荐匿名内部类,成员类局部累;如果非要在一个类的内部使用,最好就是静态的内部类。
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.prepare()发导致的
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//拿到当前loop拥有的handler
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public final boolean sendMessage(Message msg)
{
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) {
//为该message指定处理这个消息的handler,后面在处理这个消息的时候
//就是在循环中拿到这个handler去处理这个消息,所以这一步很重要。
msg.target = this;
//由于我们分析的是无参数的构造mAsynchronous=false
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//消息入队列
return queue.enqueueMessage(msg, uptimeMillis);
}
enqueueMessage有两个操作,一个是没有队列的时候构造队列的头,另外一个就是入队操作。
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(TAG, 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) {
//没有队列,创建队列的头
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// 根据msg.when的先后,找到合适的插入位置,先执行的在队列前面;
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;
}
消息的入队操作介绍完来,下面介绍消息是什么时候分发给处理者的
还记得我们的消息的哪个无限的for循环吗,我再贴一下代码
for (;;) {
//取出一个消息处理,
Message msg = queue.next(); // might block
if (msg == null) {
//没有消息了就推出消息循环。
// No message indicates that the message queue is quitting.
return;
}
//把消息分发给对应的handler处理。
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
//把已经处理的消息放回到消息池里面,用来复用这个消息。这个消息吃的最大缓存熟是50。
msg.recycleUnchecked();
}
msg.target.dispatchMessage(msg);这一行就是去把消息分发给对应的handler处理
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
dispatchMessage分发最终把消息传给来handlerMessage分发,所以我们在创建Handler的时候都要复写handlerMessage方法来处理分发过来的消息;因为我们用的是无参数的
Handler的构造分发,所以mCallback是null,但是如果我们传来mCallback,可以看到回调函数的优先级是比handlerMessage分发的优先级高的。
Android的消息机制我们就介绍完来,下面我们总结一下:
- 涉及到的类有:
- Looper:主要用来创建消息队列和对消息队列进行遍历
- MessageQueue:主要用来存储Handler发过来的消息
- Message:消息的载体,主要负责携带我们发送的数据
- Handler:消息的发送者和执行者。
- Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
-
下面是Android中的消息机制在的处理流程图
Android消息机制.png
网友评论