Android 消息机制源码分析

概述

Android 的消息机制主要是指 Handler 的运行机制。Android 规定只有主线程可以访问 UI ,子线程中无法访问 UI。但是主线程中不建议进行耗时操作,因为这会引起 ANR。
系统为什么不允许子线程中访问 UI?
如果多线程并发访问,UI 控件处于不可控制的状态。如果对 UI 控件的访问上锁,首先上锁机制会让 UI 访问的逻辑变得复杂;其次会降低 UI 的访问效率,因为锁机制会阻塞某些线程的执行。所以,采用单线程模型来处理 UI 操作简单高效。

消息机制解决了我们需要从服务器获取数据后操作 UI 的矛盾。但是更新 UI 仅仅是 Handler 的一个使用场景。本质上来说,Handler 并不是专门用于更新 UI 的,它只是在更新 UI 的场景中用的比较多。Handler 的使用过程很简单,通过它可以轻松的将一个任务切换到 Handler 所在的线程中去执行。Handler 的运行需要底层的 MessageQueue 和 Looper 的支撑。

Android消息机制有哪些核心成员组成的呢?

  • MessageQueue:
    消息队列。 它的内部存储了一组消息,以队列的形式对外提供插入和删除的操作。但是内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表。
  • Looper:
    消息循环。以无限循环的形式去查询是否有新消息,如果有的话就处理消息,没有就一直等待。
  • Handler:
    消息传递的主要操作者。将消息从子线程传递到主线程然后处理消息。
  • ThreadLocal:
    不是线程,它的作用是可以在每个线程中存储数据。
    Handler 创建的时候会采用当前线程的 Looper 来构造消息循环系统,Handler 内部要使用 ThreadLocal 获取当前线程的 Looper。ThreadLocal 可以在不同的线程中互不干扰的存储并提供数据,通过 ThreadLocal 可以轻松获取每个线程的 Looper。
    注意:线程默认是没有 Looper 的,如果需要使用 Handler 必须为线程创建 Looper。主线程(UI线程 ActivityThread)被创建时就会初始化 Looper,这也是主线程中默认可以使用 Handler 的原因。

Handler分析

Handler 是Android消息机制的上层类,也是Android消息机制中我们接触最多的类。使用消息机制的时候,我们都是通过 Handler 的 post 和 send 系列方法开始的,我们从这里开始分析,借助源码,探索Android消息机制的底层原理。我们先看 post 系列方法源码:

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

public final boolean postAtTime(Runnable r, long uptimeMillis) {
    return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}

public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {
    return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}

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

我们再看 send 系列方法的源码:

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

public final boolean sendEmptyMessage(int what){
    return sendEmptyMessageDelayed(what, 0);
}

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

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

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

画个简易的图,理清这些方法之间的关系。

图中可以发现:post 系列和 send 系列的方法最终都指向了 sendMessageAtTime()方法。也就是说,send 系列的方法最终都是靠 sendMessageAtTime()方法实现的。我们看下 sendMessageAtTime 方法的实现:首先获取一个 MessageQueue,然后传给 enqueueMessage 方法,调用此方法。下面是 enqueueMessage 方法源码:

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

可以看到,最后执行的是 MessageQueue 类的 enqueueMessage 方法。我们知道 MessageQueue有两个很重要的方法:enqueueMessage 和 next。enqueueMessage 负责往消息队列里添加数据,next 负责从消息队列里取出数据。所以,handler post 和send 系列的方法做的事情就是往 MessageQueue 里面添加数据。

现在我们知道数据是怎么添加到消息队列里面去的了,这是消息传递的第一步,那第二步就是把消息取出来进行处理,虽然处理消息依然是 handler 的事,但把消息取出来却是 Looper 默默一直干的事。我们分析下 Looper。

Looper

Looper 是让我们整个消息机制循环起来的核心类。普通的线程是没有消息队列的,也是无法使用 Handler 的(主线程:ActivityThread 被创建的时候默认初始化 Looper ,这也是我们可以直接在主线程使用 Handler 的原因)。正是借助 Looper 让线程成为 Looper 线程,线程和 Looper 绑定后,也就有了消息队列,因为消息队列 (MessageQueue) 是放在 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));
}

prepare 方法很简单,里面就做了一件事,就是给变量 sThreadLocal 设置值。sThreadLocal 变量是一个 ThreadLocal 类型的变量:

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

如果对 ThreadLocal 不太了解可以先看下这篇文章 Android消息机制-ThreadLocal。我们知道,ThreadLocal 的作用就是保证使用 ThreadLocal 存储的数据只属于当前线程,其他线程无法获取。sThreadLocal 泛型是 Looper,所以这个变量会存储一个 Looper 对象。prepare() 方法是调用了 sThreadLocal 的 set 方法存储了一个新建的 Looper 对象。存储之前会判断是否已经有了 Looper 对象,如果已经有了,会抛异常。所以,一个线程中只能有一个 Looper 对象。这样就保证了这个 Looper 对象只属于当前线程,而且只有一个 Looper 对象。我们看看 new Looper(quitAllowed)这步都干了什么:

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

可以看到,首先,构造方法是私有的,就是在别的类中不能实例化 Looper 对象。方法中就是给两个变量赋值,一个是我们的消息队列 mQueue,一个是线程对象 mThread。此时,我们的 Looper 对象有了消息队列,而且获取到了当前线程。然后看下 loop() 方法:

/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 */
public static void loop() {
    final Looper me = myLooper();//1、获取looper对象
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;//2、获取消息队列

    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
        Message msg = queue.next(); // 取出队列中的消息
        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.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        try {
            msg.target.dispatchMessage(msg);//消息不为空执行此处
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

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

这里就出现了我们一直想寻找的东西啦。首先 1 处通过 myLooper()方法获取到存储的 looper 对象:

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

然后 2 处通过获取到的 Looper 对象获取到对象中的消息队列。获取到消息队列后,通过一个不设参数的 for 循环方法不断取出消息,如果消息不为空,就执行:

msg.target.dispatchMessage(msg);

msg我们都知道是 Message,那么 msg.target 是什么呢?哈哈,还记得我们前面分析的 Handler 把消息存入消息队列的过程吗,Handler 的存储方法:enqueueMessage 方法:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;//这里把handler对象赋给了target
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

所以,这个 target 就是我们的 handler 对象,next 方法取出消息后就调用了我们 handler 对象的 dispatchMessage() 方法。这里就是我们能不断处理消息的关键: Looper 对象一直在幕后不断的取出消息给我们的 handler 对象,然后由 handler 对象去处理消息。

现在我们已经清楚了消息队列是什么时候构造的,消息是什么时候存入队列的,消息是怎么取出的,还差一步,我们看看 handler 是怎么处理消息的,就是我们上面取出消息后执行的方法: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 是否为空,callback 是什么呢?是我们调用 post(Runnable r) 方法传入的 runnable 对象:

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 系列方法,如果为空则用的是 send 系列方法。第二步,判断 mCallback 是否为空,mCallback 又是什么呢?

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

看到这里,大家应该明白了吧,终于看到我们熟悉的处理消息的方法了:handleMessage。mCallback 是否为空对应的是 Handler 类的不同构造方法。

public Handler() {
    this(null, false);
}
public Handler(Callback callback) {
    this(callback, false);
}

我们在创建 handler 对象的时候,可以直接传入一个匿名内部类去实现 handleMessage 方法;也可以构造方法不传参,然后去实现 handlerMessage 方法。所以,在 dispatchMessage 方法中,判断如果 mCallback 为空的话,执行 handleMessage 方法。这样,我们就走到了 handleMessage 方法,就可以按照我们的业务逻辑去处理消息了。

最后画一张图总结一下整个流程:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值