Handler、Looper、MessageQueue消息机制原理

QuibblerQuibbler 2020-04-22 约 22 分钟 1896 次阅读

Handler、Looper、MessageQueue消息机制原理

Android消息机制是安卓开发中非常重要的,在子线程中处理完I/O、网络请求,使用Handler切回到主线程处理。


1、Looper

Android Looper设计的非常巧妙,使用ThreadLocal实现这个特性。关于ThreadLocal参考另外一篇Thread和ThreadLocal。

1.1、主线程Looper

一个Application启动的时候就会开启一个主Looper,因为比较特殊,所以可以在任何一个地方通过Looper.getMainLooper()方法获取到。来看看主线程中Looper的初始化,从ActivityThread中的main()静态方法启动一个应用:

public static void main(String[] args) {
        AndroidOs.install();
        ...
        Looper.prepareMainLooper();
        ...
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

主线程Looper已经在Application启动的时候初始化prepareMainLooper(),无需要开发者调用此方法,由框架负责调用初始化主线程Looper。

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

        主线程的sMainLooper在APP运行起来时必然已经初始化,

@UnsupportedAppUsage
    private static Looper sMainLooper;  // guarded by Looper.class

        所以提供了静态方法getMainLooper()可以在应用任何地方获取主线程Looper实例:

/**
     * Returns the application's main looper, which lives in the main thread of the application.
     */
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

1.2、初始化Looper

        其它子线程默认是没有Looper,如果需要先为所在线程准备一个Looper,使用prepare()静态方法。

/** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        prepare(true);
    }


        注意只能调用一次,不要重复在同一个线程中prepare(),如果线程已经有了一个Looper再次设置一个Looper会抛出异常。

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()方法给当前ThreadLocal设置Looper实例。

// sThreadLocal.get() will return null unless you've called prepare().
    @UnsupportedAppUsage
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

Looper构造函数初始化一个Looper实例,设置给当前线程的sThreadLocal变量。无论设置给哪个ThreadLocal,最终都是set给当前Thread中的ThreadLocal对象副本。

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

Looper内部持有一个MessageQueue对象mQueue,直接创建Handler的时候,Handler会通过ThreadLocal获取到当前线程的Looper,进而获取到Looper中的mQueue。

1.3、开启循环

开启Loop循环,在当前线程中运行消息队列。该方法里面有一个无限循环,有消息就通过msg.target.dispatchMessage(msg)发送给Handler处理。注意调用MessageQueue的next()方法获取队列中的Message,也可能阻塞吗,后面MessageQueue会说到。

①有消息:一直循环

②无消息:返回。但是注意,前面MessageQueue的next()方法没有消息也会一直阻塞,除非调用quit()方法,让MessageQueue停止循环,否则loop()也是不会走到return的。

/**
     * 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;
        ...
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                return;
            }
            try {
                msg.target.dispatchMessage(msg);
            }
            ...
            msg.recycleUnchecked();
        }
    }

1.4、退出Looper循环

注意一定要在线程退出的时候调用Looper的quit()方法,否则会造成内存泄漏。方法中还是通过调用MessageQueue的quit()方法退出,后面再看MessageQueue原理。

public void quit() {
        mQueue.quit(false);
    }

2、MessageQueue

消息队列,但实际上是用链表实现的,并不真的是“队列”。MessageQueue结合Looper完成消息传递,普通开发工程师基本上用不到,但是还是需要对这个队列的实现原理有所了解的。

2.1、next()

最重要的一个方法就是next(),内部也有一个无限循环,会不断的从消息队列中取Message对象。

①有消息:返回消息。

②无消息:一直不断的循环;阻塞前面调用方法loop()。

Message next() {
        ...
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
        
        for (;;) {
                ...
                Message msg = mMessages;
                
                if (msg != null) {
                    if (now < msg.when) {
                        ...
                    } else {
                        ...
                        return msg;
                    }
                } 
                
                if (mQuitting) {
                    dispose();
                    return null;
                }
                ...
        }
    }

2.2、enqueueMessage(Message msg, long when)

Handler发送的Message最终通过该方法,放入消息队列中。这里和链表插入有关,实现Message延迟的核心代码,详解见Handler消息延迟原理。

3、Handler

构造一个Handler,除了使用Looper还可以借助Callback。需要重写Handler中的handlerMessage()方法,处理不同的Message。

3.1、创建Handler

①主线程中可以直接创建Handler,开头已经知道主线程的Looper默认初始化的。

②如果在子线程中使用Handler,要么使用主线程的Looper:

Handler handler = new Handler(Looper.getMainLooper()){
       @Override
       public void handleMessage(@NonNull Message msg) {
                        
      }
   };

要么必须先初始化所在子线程的Looper,开启Looper,才能创建子线程的Handler:

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

否则之间创建Handler会奔溃报错:java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

Handler handler = new Handler();

Handler构造方法:

public Handler() {
        this(null, false);
    }
    
    public Handler(@Nullable Callback callback, boolean async) {
        ...
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        ...
    }

构造Handler的时候,就已经将内部的几个重要成员变量初始化:mLooper、mQueue。

3.2、sendMessage(Message msg)

发送消息,经过调用链跳转:sendMessage->sendMessageDelayed->sendMessageAtTime     

public boolean sendMessageAtTime(@NonNull 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);
    }

3.3、enqueueMessage(...)

发送的消息最终是到了成员变量mQueue消息队列中。这里会将Message中的target设置为当前发送的Handler。

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

3.4、dispatchMessage(Message msg)

Looper在loop()方法中循环的时候,会将消息发送会Handler处理。

try {
        msg.target.dispatchMessage(msg);
    }

分发消息:

public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

3.5、handleMessage(Message msg)

到这里是不是发现很熟悉呢?没错,就是创建Handler的时候重写的handleMessage()方法,这个方法最终在Looper所在的线程loop()循环中被调用,通过Handler可以将子线程的数据传回主线程,更新UI。另见Handler中Callback的作用一文。

/**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(@NonNull Message msg) {
        
    }

推荐博客:

        Android 消息处理机制(Looper、Handler、MessageQueue,Message)

        Android--异步消息处理机制(Handler、Looper、Message、MessageQueue)

        Android Handler机制之Message的发送与取出

        Handler一定要在主线程实例化吗?


相关推荐

精选
ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager
Android

ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager

ViewPager1、ViewPagerandroidx.viewpager.widget.ViewPager,Android中使用非常广泛的控件,可以说是APP必备:首次打开引导页、页面Banner广告等。常用方法:setAdapter() 设置适配器setOffscreenPageLimit() 设置缓存的页面个数,默认是 1setCurrentItem() 跳转到特定的页面setOnPage

1.1k
获取Android内置WebView内核版本
Android

获取Android内置WebView内核版本

获取Android内置WebView内核版本竟然能遇到这样奇葩的事情,网页用的技术过于新颖,以至于只支持高版本Chromium内核的,低版本安卓系统中内置的内核版本较低,无法加载前端页面。1、设置查看在系统设置里 > 应用 > 应用管理 > 显示系统应用,查看WebView组件:2、页面查看通过WebView发起的网络请求,都会带上浏览器的UA,通常页面都可以通过UA判断浏览器的内核版本。这里有两

1.1w
Android 14适配总结
Android

Android 14适配总结

Android 14适配总结毕业工作至今已经适配了三个Android大版本,从Android 11到Android 12、再到Android 13。2023年,Google即将推出的Android 14,上半年已经开始第一批适配。现在,第四个Android版本已经适配完,总结记录一下。1、Android 14计划Google一般会在2月份对外发布预告,同时放出开发者预览版。“拉通”各大平台、厂商以

1.1w