Handler消息机制 源码解读

基本概念

Handler消息机制的作用

大家知道子线程没有办法对UI界面上的内容进行操作,如果操作,将抛出异常:CalledFromWrongThreadException,为了让子线程能间接操作UI界面,Android中引入了Handler消息传递机制,通过Handler切换到主线程进行UI操作。

Handler、Looper、MessageQueue、Message的关系是什么?

  • Handler用于发送和处理消息。而发出的Message经过一系列的周转后,最终会传递回Handler中,最后更新UI。
  • Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之前交换数据。
  • MessageQueue是消息队列,用于存放Message。Message在消息队列中,等待Looper取出。每个线程中只会有一个MessageQueue对象。
  • Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入一个无限循环中,每当MessageQueue中存在一个Message,Looper对象就会将其取出,传递到Handler中进行处理。每个线程中只会有一个Looper对象。

Handler消息机制疑问

从上面的基本概念中,我们不难会产生疑问,比如:

  • 在Message的周转过程中,是怎么切换到主线程的?
  • 如果当前线程中new了多个Handler,它们发送消息后会错乱吗?会不会找错Handler对象。
  • Handler的post方法和sendMessage有什么区别?
  • 怎么保证每个线程中只能存在一个Looper和MessageQueue?
  • 子线程为什么不能直接new Handler?
  • 主线程为什么不用手动创建Looper?
  • 主线程中的Looper无限循环,为什么没有造成ANR?

下面,我将从源码角度,对以上疑问进行探索。看完本篇博客,相信你心里就会有答案了。

初识API

Message

我们先来简单认识一下Message

    public int what;
    public int arg1;
    public int arg2;
    public Object obj;

上面几个的用法,大家都知道,就不用介绍了。我们来看看下面的。

    Handler target;
    Runnable callback;

Message中可以保存Handler对象,也可以保存Runnable,具体用法看完本篇博客就知道了。

还记得Message.obtain用法吗?

 public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0;
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

因为Message被处理完过后,是不会直接回收的,默认会保存一定数量的Message以供复用。我们可以使用Message.obtain复用,以免创建多余的Message。

MessageQueue

MessageQueue是什么?我们可以给它理解成一个集合,比如List,我们可以添加消息,也可以读取消息,移除消息。当然MessageQueue的内部是通过一个单链表的数据结构来实现的,理解起来可能有点费劲,我们只需知道有两个重要的方法enqueueMessage插入消息,next取出消息。

  //======================插入消息=================================
    boolean enqueueMessage(Message msg, long when) {
            //...
            //省略了部分代码
            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;
    }

    //======================取出消息=================================

Message next() {
             //...
            //省略了部分代码
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            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.
                    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;
                }
              //...
            //省略了部分代码
        }
    }

工作原理

发送消息(将Message提交到MessageQueue)

Handler平时用的都比较多,一般都会直接使用 mHandler.sendMessage进行发送消息,然后重写Handler的handleMessage进行接收和处理消息。那么 mHandler.sendMessage 到底做了什么事呢?

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

我们可以发现,在Handler内部,其实调用的是sendMessageDelayed,然后sendMessageDelayed中又调用了sendMessageAtTime

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

sendMessageAtTime中我们看到了什么?没错MessageQueue,而且MessageQueue不可为空,否则会抛出异常,你可能为疑问,这个MessageQueue是从哪里来的,不要急,下面马上就会介绍,在Handler的构造方法那里就能看到。我们暂且不管MessageQueue是怎么工作的,我们只需知道,我们当前的任务是将Message塞进MessageQueue中,我们接下来看看enqueueMessage中做了什么。

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

我们可以发现,这里调用了MessageQueue中的queue.enqueueMessage方法将Message插入到队列中去,到此为止,我们完成了第一步。

我们还能发现这一行msg.target = this;,没错,将当前的Handler对象绑定给了Message,这也就能解释,为什么Message一番周转之后,仍然知道该传递给哪个Handler对象。

取出消息(将Message从MessageQueue中取出)

Message塞给了MessageQueue后,现在就该轮到Looper登场了。Looper是怎么从MessageQueue取出中取出Message的呢。

Looper的属性

先来简单看一下Looper的三个属性。

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;
    final MessageQueue mQueue;

可以看出Looper中会绑定一个对应的MessageQueue,还有一个线程变量sThreadLocal

子线程中为什么不能直接创建Handler

在介绍之前,我们先来解释下,为什么不能直接在子线程中new Handler。

大家都知道在线程中使用Handler如下,但是可能不知道为什么要这么写。

  Looper.prepare();
  Handler handler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
  Looper.loop();

那么,我们先来看一下Handler构造方法,Handler构造方法有两种,一种是显示指定Looper对象的,另一种是不显示指定Looper的(会默认获取当前线程的Looper),所有不显示指定Looper的构造方法都会在内部转为调用以下构造。

public Handler(Callback callback, boolean async) {
        //...
        //省略了部分代码
        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;
    }

我们可以发现只要mLooper为空,就会抛出异常。不为空的话就连同MessageQueue赋值给当前Handler,你可能又想问,Looper的MessageQueue是怎么来的,莫急,待会会介绍Looper的构造方法。我们先看看Looper.myLooper()方法

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

sThreadLocal是什么鬼?

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

看到这里终于明白了,原来用了ThreadLocal啊,ThreadLocal是Java用来存储线程变量的api。也就是说,假如我们在主线程使用sThreadLocal存储了一个Looper,在子线程也使用了sThreadLocal存储了一个Looper,他们互不干扰。每个线程取出来的值都是不一样的。这也就保证了,每一个线程都有一个单独的Looper。

那么如何在子线程使用Handler呢?相信大家都有思路了,只要保证sThreadLocal.get()能取到值就行,我们可以在new 之前给当前线程一个Looper对象。api中已经提供了方法,Looper.prepare()如下。

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

而且也可以看出,每个线程中只能设置一个Looper对象,否则还是会抛出异常。为什么要强制只能有一个Looper对象呢?当然是因为Android的单线程模型啊。如果允许多个Looper那么和在子线程中直接处理没有任何区别,会导致一系列的并发问题。

sThreadLocal.set(new Looper(quitAllowed))给当前线程绑定一个Looper,我们来看一下Looper的构造。

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

从源码可以看出,与此同时,也给Looper绑定了一个MessageQueue对象。

Looper取消息

那么,Looper到底是怎么从MessageQueue中取出Message的呢。我们来看下 Looper.loop()

    public static void loop() {
        final Looper me = myLooper();//获取当前线程的Looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn‘t called on this thread.");
        }
        final MessageQueue queue = me.mQueue; //获取Looper对应的消息队列
         //...
         //省略了部分代码
        for (;;) {//死循环
            Message msg = queue.next();  //从消息队列中取出一下个Message
              //...
             //省略了部分代码
            msg.target.dispatchMessage(msg);//msg.target就是Handler,调用Handler的dispatchMessage方法处理消息

              //...
             //省略了部分代码
            msg.recycleUnchecked();//回收Message
        }
    }

流程非常清晰,通过一个死循环,不停调用MessageQueuenext()方法,取出Message,然后看到了没msg.target,前面我们发送消息时,绑定的Handler对象。经历了一番周转变换了线程,又交给了Handler对象的dispatchMessage中进行处理消息。

处理消息(Handler调用dispatchMessage处理消息)

在介绍处理消息之前,我们先来认识一下Handler的其他使用方法。我们知道只要重写Handler的handleMessage方法,然后就可以接收消息了。但是,我如果不想重写handleMessage呢?有没有其他方法?当然是有的,请往下看。

Handler的构造参数

前面我也提到了,Handler的构造方法分为两种,显示指定Looper与否,其实这两种都是一样的,没有本质上的区别。但是我们忽略了Handler构造方法中的其他参数,现在再来看一遍。

public Handler(Callback callback, boolean async) {
        //...
        //省略了部分代码
        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;
    }

有没有发现一个Callback参数,其实它是一个接口,也是用于处理消息的。

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

使用mHandler.post发送消息。

mHandler.sendMessage这种方法大家都很常用,也很简单。大家都知道Handler有个post方法,那么它和sendMessage有什么区别呢?我们来看一下源码。

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

从上面可以看出,使用post,仍然会包装为Message,然后调用sendMessageDelayed进行发送消息。先来看一下到底是怎么包装的,如下

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

包装给了一个callback,就是前面提到的Runable。

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

看到没有,if (msg.callback != null),也就是说,如果是使用post发送的,会调用handleCallback进行处理。

    private static void handleCallback(Message message) {
        message.callback.run();
    }

直接走了Runable的run方法。不会走Handler的handleMessage

然后 if (mCallback != null)判断那个接口有没有实现,如果实现了,直接走接口。最后才是调用Handler内部的handleMessage方法。

最后

  • 主线程为什么不用手动创建Looper?

我们来看一个类ActivityThread,这个类是Android的入口,我们可以找到一个久违的方法public static void main(String[] args)

    public static void main(String[] args) {
       //...
       //省略了部分代码

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        //...
       //省略了部分代码
        Looper.loop();

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

从上面可以看出,Android在启动一个应用时,已经默认给主线程开启了Looper。

  • 主线程中的Looper无限循环,为什么没有造成ANR?

什么情况下会造成ANR呢?只有当主线程的Handler处理一个消息耗时过长才会ANR。

Looper无限循环,不是一个简单的死循环,当消息队列中没有消息时,就会阻塞休眠,释放cpu资源。

我们Activity之所以有生命周期,也依赖于Handler,是因为ApplicationThread根据系统ActivityManageService发来的事件,然后发送消息来调度生命周期的。以下是ActivityThread中处理消息相关的部分代码。

 public void handleMessage(Message msg) {
              //..
            //省略了部分代码
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;

                case PAUSE_ACTIVITY:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                    handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2,
                            (msg.arg1&2) != 0);
                    maybeSnapshot();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
             //..
             //省略了部分代码
            }

        }

END

时间: 2024-07-29 14:45:55

Handler消息机制 源码解读的相关文章

Android Handler消息机制源码解析

好记性不如烂笔头,今天来分析一下Handler的源码实现 Handler机制是Android系统的基础,是多线程之间切换的基础.下面我们分析一下Handler的源码实现. Handler消息机制有4个类合作完成,分别是Handler,MessageQueue,Looper,Message Handler : 获取消息,发送消息,以及处理消息的类 MessageQueue:消息队列,先进先出 Looper : 消息的循环和分发 Message : 消息实体类,分发消息和处理消息的就是这个类 主要工

Handler消息机制源码分析

public static final Looper myLooper() { return (Looper)sThreadLocal.get(); } 先来个Handler执行过程的总结: 1. Looper.prepare()方法 为当前线程绑定looper, 在looper构造方法中创建一个messageQueue 2. 创建handler 重并写handleMessage方法 3. 使用handler发送消息,最终消息都会发送至messageQueue对象中,在messageQueue当

拨云见日---android异步消息机制源码分析

做过windows GUI的同学应该清楚,一般的GUI操作都是基于消息机制的,应用程序维护一个消息队列,开发人员编写对应事件的回调函数就能实现我们想要的操作 其实android系统也和windows GUI一样,也是基于消息机制,今天让我们通过源码来揭开android消息机制的神秘面纱 谈起异步消息,就不能不提及Handler,在安卓中,由于主线程中不能做耗时操作,所以耗时操作必须让子线程执行,而且只能在主线程(即UI线程)中执行UI更新操作,通过Handler发送异步消息,我们就能更新UI,一

[Android]简略的Android消息机制源码分析

相关源码 framework/base/core/java/andorid/os/Handler.java framework/base/core/java/andorid/os/Looper.java framework/base/core/java/andorid/os/Message.java framework/base/core/java/andorid/os/MessageQueue.java libcore/luni/src/main/java/java/lang/ThreadLo

Android消息机制源码分析

本篇主要介绍Android中的消息机制,即Looper.Handler是如何协同工作的: Looper:主要用来管理当前线程的消息队列,每个线程只能有一个Looper Handler:用来将消息(Message)插入到当前线程的消息队列,并负责分发Looper中的消息,将消息发送到当前线程执行 具体关系图如下所示: 接下来我们来分析一下Looper和Handler的源码,了解一下其中的奥妙. 首先我们从一个程序运行的入口来分析,源码如下: public static void main(Stri

AsyncTask异步任务 源码解读

之前我们介绍了Handler的一些基本用法,也解读了Handler的源码.通过Handler我们可以简便的切换到主线程进行UI操作.而AsyncTask的出现使我们不用去关心线程管理和切换的一些细节,我们可以更轻松的去操作UI. 基本概念 AsyncTask异步任务的作用 AsyncTask,见名之意,异步任务.允许我们在后台做一些耗时操作,然后切换到主线程更新,而且这一过程变得非常简便.一提到异步任务,我们的第一反应就是多线程.假如我们现在需要去下载一张图片,然后在界面上显示,如果没有Asyn

Android -- 消息处理机制源码分析(Looper,Handler,Message)

android的消息处理有三个核心类:Looper,Handler和Message.其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道,因此我没将其作为核心类.下面一一介绍: Looper Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程.所谓Looper线程就是循环工作的线程.在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务,这就是Lo

从Handler+Message+Looper源码带你分析Android系统的消息处理机制

引言 [转载请注明出处:从Handler+Message+Looper源码带你分析Android系统的消息处理机制 CSDN 废墟的树] 作为Android开发者,相信很多人都使用过Android的Handler类来处理异步任务.那么Handler类是怎么构成一个异步任务处理机制的呢?这篇 博客带你从源码分析Android的消息循环处理机制,便于深入的理解. 这里不得不从"一个Bug引发的思考"开始研究Android的消息循环处理机制.说来话长,在某一次的项目中,原本打算开启一个工作线

KClient——kafka消息中间件源码解读

kclient消息中间件从使用角度上开始入手学习 kclient-processor 该项目使用springboot调用kclient库,程序目录如下: domainCat : 定义了一个cat对象Dog : 定义了一个Dog对象handler : 消息处理器AnimalsHandler : 定义了Cat和Dog的具体行为KClientApplication.java : Spring boot的主函数--程序执行入口KClientController.java : Controller 文件t