前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >源码分析——Android Handler是如何实现线程间通信的

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

作者头像
阳仔
发布2019-07-31 17:20:58
7720
发布2019-07-31 17:20:58
举报
文章被收录于专栏:终身开发者终身开发者

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

0x00 Handler 使用

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

代码语言:javascript
复制
//定义
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)

代码语言:javascript
复制
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构造函数

代码语言:javascript
复制
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()

代码语言:javascript
复制
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对象。

代码语言:javascript
复制
/**
 * 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()方法。

代码语言:javascript
复制
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对象

代码语言:javascript
复制
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

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

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

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

0x05 启动循环 loop()

代码语言:javascript
复制
/**
 * 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实例。

代码语言:javascript
复制
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)后便走到了以下流程

代码语言:javascript
复制
/**
 * 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对象,而通过 loop()启动一个循环不停地读取队列中 Message。但是Android系统是什么时候启动了主线程的消息循环呢?

要理解这一点就必须进入Android应用程序的入口 ActivityThreadmain方法。

代码语言:javascript
复制
public static void main(String[] args) {
    //...
    Looper.prepareMainLooper();

    //...
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

可以看出 main方法中先后执行了 Looper.prepareMainLooper()方法和 Looper.loop()方法。正常情况下 main方法不会退出,只有 loop()方法发生异常后将会抛出 RuntimeException

0x07 Looper.prepareMainLooper()

代码语言:javascript
复制
/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

prepareMainLooper()方法其实是调用了 prepare()方法。

当我们启动应用时系统就调用了 prepareMainLooper()并在主线程中绑定了一个 Looper对象。

这时候我们回过来看看一开始的 Handler使用方式。在主线程中我们创建了 Handler对象,在 Handler构造函数中初始化了 Looper(即获取到了绑定在主线程中的 Looper对象)。当在子线程 MyThread中通过 mHandler.sendMessage(msg)方法发送一个消息时就把 Message放在与主线程绑定的 MessageQueue中。这样在子线程中使用 Handler就实现了消息的通信。

可以简单的使用以下类图表示,每个线程都由一个 Handler,每个 Handler 都是与当前所在线程的 Looper 绑定

0x08 主线程是否会阻塞

0x06中知道在 ActivityTheadmain方法中启动了一个死循环。那主线程是不是就一直阻塞在这里呢?其实不然。可以看到 ActivityThread类里面有一个自定义的 Handler对象 mH,在这里对象中 handleMessage()回调中定义了 Activity的各种交互如管理 Activity生命周期,启动 service,显示 window等,都是通过 Handler进行处理的。同时可以看出只有当应用退出 EXIT_APPLICATION之后才回调用 Looper.quit()停止消息循环。

代码语言:javascript
复制
public void handleMessage(Message msg) {
    //...
    switch (msg.what) {
        case LAUNCH_ACTIVITY: {
            //...
            handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        } break;
        //...
        case PAUSE_ACTIVITY: {
            //...
            handlePauseActivity((IBinder) args.arg1, false,
                    (args.argi1 & USER_LEAVING) != 0, args.argi2,
                    (args.argi1 & DONT_REPORT) != 0, args.argi3);
            //...
        } break;

        //...
        case SHOW_WINDOW:
            //...
            handleWindowVisibility((IBinder)msg.obj, true);
            //...
            break;
        //...
        case EXIT_APPLICATION:
            if (mInitialApplication != null) {
                mInitialApplication.onTerminate();
            }
            Looper.myLooper().quit();
            break;
        //...
    }
    //...
}

0x09 总结

当创建 Handler时将通过 ThreadLocal在当前线程绑定一个 Looper对象,而 Looper持有 MessageQueue对象。执行 Handler.sendMessage(Message)方法将一个待处理的 Message插入到 MessageQueue中,这时候通过 Looper.loop()方法获取到队列中 Message,然后再交由 Handler.handleMessage(Message)来处理。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 终身开发者 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x00 Handler 使用
  • 0x01 sendMessage(Message)
  • 0x02 Handler构造函数
  • 0x03 enqueueMessage()
  • 0x04 prepare() 创建 Looper
  • 0x05 启动循环 loop()
  • 0x06 启动主线程的消息循环
  • 0x07 Looper.prepareMainLooper()
  • 0x08 主线程是否会阻塞
  • 0x09 总结
相关产品与服务
消息队列 CMQ 版
消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档