资讯专栏INFORMATION COLUMN

Handler、Looper与MessageQueue源码分析

forrest23 / 245人阅读

摘要:在中可以通过来更新主线程中的变化,更新只能在主线程中进行更新,而为了让其他线程也能控制的变化,提供了一种机制与一同协作来达到其他线程更新的目的。这样一来就通过联系上了所绑定的线程,即为主线程。

Android中可以通过Handler来更新主线程中UI的变化,更新UI只能在主线程中进行更新,而为了让其他线程也能控制UI的变化,Android提供了一种机制HandlerLooperMessageQueue一同协作来达到其他线程更新UI的目的。

一般我们会在主线程中通过如下方法定义一个Handler

private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            tv.setText("mHandler change UI");
            super.handleMessage(msg);
        }
    };

一般都见不到LooperMessageQueue的,那么它们都是在哪里调用与如何协作的呢?在主线程不会显式的调用Looper而是会在ActivityThread.main方法中默认调用。

ActivityThread.main
public static void main(String[] args) {
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();
 
        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);
 
        Environment.initForCurrentUser();
 
        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());
 
        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
         TrustedCertificateStore.setDefaultUserDirectory(configDir);
 
        Process.setArgV0("");
 
        Looper.prepareMainLooper();//创建Looper
 
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
 
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
 
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
 
        // End of event ActivityThreadMain.
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();//开启Looper循环
 
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

如上代码,调用了Looper.prepareMainLooper()方法,在主线程中创建了一个Looper,不信的话我们再查看该方法做了什么

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并赋给sThreadLocal
    }
 
    /**
     * 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();
        }
    }
     
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

prepareMainLooper方法中调用了prepare而通过prepare会发现它其实就是创建了一个Looper,并把它赋给了sThreadLocal。同时可以通过myLooper方法获取当前线程中的Looper。再来看下new Looper(quitAllowed)初始化了什么

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

在这里我们终于看到了MessageQueue了,它创建了一个MessageQueue。该消息队列就是用来保存后续的Message。再回到ActivityThread.main方法中,发现它调用了Looper.loop()是用来开启Looper循环的,监听消息队列MessageQueue中的消息。

loop

我们来看下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;//获取消息队列
 
        // 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.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);//通过Handler分发消息
            } 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中首先获取了当前所在线程的Looper,同时也获取到了Looper中的MessageQueue,说明Looper已经与当前的线程进行了绑定。在后面开启了一个for的死循环,发现它做的事件是不断的从消息队列中取出消息,最后都交给msg.target调用它的dispatchMessage方法,那么target又是什么呢?我们进入Message

Message
/*package*/ int flags;

    /*package*/ long when;
    
    /*package*/ Bundle data;
    
    /*package*/ Handler target;
    
    /*package*/ Runnable callback;
    
    // sometimes we store linked lists of these things
    /*package*/ Message next;

发现它就是我们熟悉的Handler,说明最后调用的就是Handler中的dispatchMessage方法,对消息的分发处理。这样一来Handler就通过Looper联系上了Looper所绑定的线程,即为主线程。

Handler
public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class 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,同时也获取了Looper中的消息队列。当然如果所处线程的Looper为空的话就会抛出异常,这就解释了为什么在非主线程中创建Handler要分别调用Looper.prepareLooper.loop而主线程则不需要,因为它默认已经调用了。

dispatchMessage
public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
private static void handleCallback(Message message) {
        message.callback.run();
    }    

回到前面,对于dispatchMessage的处理,首先判断msg.callback是否为空,这里callback通过上面的Message应该能知道他就是一个Runnable,如果不为空则直接调用Runnablerun方法。否则调用HandlerhandleMessage方法.而这个方法相信大家已经很熟悉了,对事件的处理都是在这个方法中执行的。因为通过前面我们已经知道了Handler已经联系上了主线程,所以handleMessage中的处理自然相对于在主线程中进行,自然也能更新UI了。通过这里我们能把Looper比作是一个桥梁,来连接Looper所在的线程与Handler之间的通信,同时管理消息队列MessageQueue中的消息。那么前面的Runnable又是如何不为空的呢?我们使用Handler有两种方法,一种是直接创建一个Handler并且重写它的handleMessage方法,而另一种可以通过Handler.post(Runnable)来使用,这样事件的处理自然就在run方法中实现。

上面介绍了Handler是如何联系上了需要操作的线程与对消息是如何取出与处理的。下面来谈谈消息是如何放入到Looper中的MessageQueue中的。

 sendMessageAtTime

通过Handler发送消息的方式很多,例如:sendMessagesendEmptyMessagesendMessageDelayed等,其实到最后他们调用的都是sendMessageAtTime方法。所以还是来看下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则就是调用了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中的queue.enqueueMessage(msg, uptimeMillis)将消息加入消息队列,同时这段代码msg.target = this 将当前的Handler赋给了msg.target,这就是前面所说的Looper.loop方法中调用的Handler。这样就把消息放到了MessageQueue中,进而通过前面所讲的loop来取出消息进行相应的处理,这样就构成了整个对消息进行处理的系统。这也是使用Handler内部所发生的原理。好了HandlerLooperMessageQueue它们之间的联系基本就是这些了。我也简单画了张图希望有所帮助

总结

来总结下它们之间的流程。首先创建Handler而在Handler所处的线程中必须要有一个Looper,如果在主线程中默认帮我们实现了,其他线程必须调用Looper.prepare来创建Looper同时调用Looper.loop开启对消息的处理。每个Looper中都有一个MessageQueue它是用来存储Message的,Handler通过post或者send..等一系列操作通过Looper将消息放入到消息队列中,而Looper通过开启一个无限的循环来一直监听着消息的处理,不断从MessageQueue中取出消息,并交给与当前Looper所绑定的handlerdispatchMessage进行分发,最后根据情况调用Runnablerun或者HandlerHandlerMessage方法对消息进行最后的处理。

其它分享:https://idisfkj.github.io/arc...

关注

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/13281.html

相关文章

  • Android之Handler消息传递机制详解

    摘要:异步消息传递所涉及的相关概念主线程线程,程序启动时自动创建。消息接受处理的对象,存储需要操作的消息。至此,关于的异步消息传递机制的解析就完成了。 前言 在Android开发中,多线程应用是非常频繁的,其中Handler机制随处可见. 下面就本人对Handle的一些理解与大家一起分享,共同回顾下Handle异步消息传递机制。 1.Handler是什么? Handler是一套在 An...

    explorer_ddf 评论0 收藏0
  • Android之消息机制问题

    摘要:通过向消息池发送各种消息事件通过处理相应的消息事件。消息泵通过不断地从中抽取,按分发机制将消息分发给目标处理者。也称之为消息队列,特点是先进先出,底层实现是单链表数据结构。目录介绍 6.0.0.1 谈谈消息机制Hander作用?有哪些要素?流程是怎样的?简单说一下你的看法! 6.0.0.2 为什么一个线程只有一个Looper、只有一个MessageQueue,可以有多个Handler? 6...

    番茄西红柿 评论0 收藏0
  • Android之消息机制问题

    摘要:通过向消息池发送各种消息事件通过处理相应的消息事件。消息泵通过不断地从中抽取,按分发机制将消息分发给目标处理者。也称之为消息队列,特点是先进先出,底层实现是单链表数据结构。目录介绍 6.0.0.1 谈谈消息机制Hander作用?有哪些要素?流程是怎样的?简单说一下你的看法! 6.0.0.2 为什么一个线程只有一个Looper、只有一个MessageQueue,可以有多个Handler? 6...

    Tony_Zby 评论0 收藏0
  • android消息处理源码分析

    摘要:如果此队列中头部元素是空的队列,一般是第一次,或者此消息不是延时的消息,则此消息需要被立即处理,将该消息作为新的头部,并将此消息的指向旧的头部。消息接口不为执行接口接口不为执行接口方法处理消息一、简介消息处理机制主要涉及到这几个类:1.Looper2.MessageQueue3.Message4.Handler 二、源码分析 Looper.class的关键源码: //保存Looper...

    番茄西红柿 评论0 收藏0
  • Android多线程之HandlerLooperMessageQueue源码解析

    摘要:在主线程中初始化的会默认与主线程绑定在一起,所以此后在处理时,方法的所在线程就是主线程,因为能用于更新四可以创建关联到另一个线程的只要本线程能够拿到另外一个线程的实列输出结果是 本文的目的是来分析一下Android系统中以Handler、Looper、MessageQueue组成的异步消息处理机制,通过源码来了解一下整个消息处理流程的走向以及相关三者之间的关系。 需要先了解以下几个预备...

    FreeZinG 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<