开发者社区> hylinux1024> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

源码分析Android Handler是如何实现线程间通信的

简介: 源码分析Android Handler是如何实现线程间通信的 Handler作为Android消息通信的基础,它的使用是每一个开发者都必须掌握的。开发者从一开始就被告知必须在主线程中进行UI操作。
+关注继续查看

 

源码分析Android Handler是如何实现线程间通信的

Handler作为Android消息通信的基础,它的使用是每一个开发者都必须掌握的。开发者从一开始就被告知必须在主线程中进行UI操作。但Handler是如何实现线程间通信的呢?本文将从源码中分析Handler的消息通信机制。

0x00 Handler使用

首先看看我们平时是如何使用的Handler的。先看看以下代码

//定义Handler
Handler mHandler = new Handler(){
  public void handleMessage(Message msg){
    switch(msg.what){
      case UPDATE_UI:
        updateUI(msg);
        break;
    }
  }
};
class MyThread extends Thread{
  public void run(){
    //do same work!
    ...
    //send message
    Message msg = mHandler.obtainMessage(UPDATE_UI);
    mHandler.sendMessage(msg);
  }
}

private void updateUI(Message msg){
  //update UI
}

在子线程中sendMessage(Message)发送消息,然后在Handler的handleMessage(Message)接收消息,执行更新UI操作。那么Handler是如何把消息从MyThread传递到MainThread中来呢?我们从sendMessage()开始慢慢揭开它的面纱。

0x01 sendMessage(Message)

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) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

我们发现调用sendMessage()方法最后都走到enqueueMessage()这个方法,一开始就把当前Handler实例赋给了Message.target的属性里面,后面可以知道这个target是用来执行处理函数回调的。

enqueueMessage方法是把Message信息放入到一个MessageQueue的队列中。顾名思义MessageQueue就是消息队列。从sendMessageAtTime()方法知道这个MessageQueueHandler中的一个成员。它是在Handler的构造函数中通过Loopger对象来初始化的。

0x02 Handler构造函数

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的时候,同时也创建了Looper实例和MessageQueue引用(MessageQueue对象其实是在Looper中构造的)。Looper是何物呢?简单地说就是消息循环,这个我们稍后会分析。

0x03 enqueueMessage(MessageQueue)

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

MessageQueue中可以看到这个入列方法中有一个for循环就是把当前的需要处理Message放到队列的合适位置。因为需要处理的Message对象都有一个开始处理的时间when,这个队列是按照when排序的。

至此,Handler调用sendMessage()方法后就把Message消息通过enqueueMessage()插入MessageQueue队列中。

而这个MessageQueue是在Looper中维护的。

0x04 prepare()创建Looper

0x02中我们知道创建Handler时就使用静态方法Looper.myLooper()得到当前线程的Looper对象。

/**
 * Return the Looper object associated with the current thread.  Returns
 * null if the calling thread is not associated with a Looper.
 */
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

sThreadLocal是一个ThreadLocal类型的静态变量。什么时候会把Looper对象放在sThreadLocal中呢?通过prepare()方法。

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

继续翻阅源码知道Looper在构造函数中创建MessageQueue对象

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

调用prepare()方法将一个Looper对象放在了静态的ThreadLocal对象中。这个是一个与线程绑定的对象,且在内存中仅保存了一份引用。

使用ThreadLocal对象这一点非常巧妙,也非常重要,这是线程间通信的基础。即在线程中调用prepare()时就在该线程中绑定了Looper对象,而Looper对象中拥有MessageQueue引用。所以每个线程都有一个消息队列

这样HandlerLooperMessageQueue这几个类关系大概就可以画出来了。

Handler类图

0x05 启动循环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();
    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
        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 {
            msg.target.dispatchMessage(msg);
        } 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.
        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();
    }
}

loop()方法中有一个无限循环,不停地读取调用MessageQueuenext()方法。当next()没有返回时就阻塞在这里。当获取到MessageQueue中的消息时,就执行了处理消息的回调函数msg.target.dispatchMessage(msg)

前面0x01分析我们知道msg.target是在Handler中的enqueueMessage()进行赋值,即它指向当前的Handler实例。

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

执行msg.target.dispatchMessage(msg)后便走到了以下流程

/**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

这里就是回调handleMessage(msg)函数处理消息的地方。Handler负责将Message入列,Looper则负责循环从MessageQueue中取出需要处理的Message并交由Handler来处理。

0x06 启动主线程的消息循环

我们知道通过静态方法Looper.prepare()创建了绑定当前线程的Looper对象,

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Android 的消息(Handler)机制
Android 的消息(Handler)机制
0 0
安卓开发过程中的RatingBar、Handler以及GPS在大型项目中的使用【Android】
安卓开发过程中的RatingBar、Handler以及GPS在大型项目中的使用【Android】
0 0
【Android】开发Android应用时对于Handler消息传递与缓存问题深入运用and理解
【Android】开发Android应用时对于Handler消息传递与缓存问题深入运用and理解
0 0
关于安卓Handler内存泄漏及解决方案
Handler是安卓中常见的实现异步操作的方法,使用简单,但是操作不注意很容易造成内存泄漏。
0 0
Android Handler与Looper原理浅析
Android Handler与Looper原理浅析
0 0
【Android开发小技巧】扔掉这坑人的 Handler
大家都知道 Handler 特别坑,使用不当会造成各种问题,使用 Kotlin Coroutines + Lifecycle 可以很好地替代 Handler。
0 0
Android 彻底掌握 Handler 看这里就够了(下)
重点关注 Handler 的 post(Runnable) 与 sendMessage(Message msg) 有什么区别 Handler.post() Handler.getPostMessage() Handler.sendMessage() Handrle.dispatchMessage() Handrle.handleCallback() Looper.loop() 为什么不会阻塞主线程 MessageQueue.next() MessageQueue.nativePollOnce() android_os_MessageQueue_nativePollOnce
0 0
Android 彻底掌握 Handler 看这里就够了(上)
Handler 允许你发送和处理与线程的 MessageQueue 关联的 Message 和 Runnable 对象。每个 Handler 实例都与一个线程和该线程的消息队列相关联。当你创建一个新的 Handler 时,它会绑定到一个 Looper。它会将消息和可运行对象传递到该 Looper 的消息队列,并在该 Looper 的线程上执行它们。
0 0
【Android 异步操作】HandlerThread 示例 ( 初始化并执行 | 获取Looper | 获取 Handler | 获取消息队列 | 设置空闲队列 | 代码示例 )
【Android 异步操作】HandlerThread 示例 ( 初始化并执行 | 获取Looper | 获取 Handler | 获取消息队列 | 设置空闲队列 | 代码示例 )
0 0
【Android 异步操作】Handler 机制 ( Handler 常用用法 | HandlerThread 简介 | HandlerThread 源码注释分析 )
【Android 异步操作】Handler 机制 ( Handler 常用用法 | HandlerThread 简介 | HandlerThread 源码注释分析 )
0 0
+关注
hylinux1024
移动端开发者
文章
问答
文章排行榜
最热
最新
相关电子书
更多
Android组件化实现
立即下载
蚂蚁聚宝Android秒级编译—— Freeline
立即下载
Android插件化:从入门到放弃
立即下载