前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Handler与Looper方法源码解析

Handler与Looper方法源码解析

原创
作者头像
Android架构
修改2019-06-18 17:53:52
7050
修改2019-06-18 17:53:52
举报
文章被收录于专栏:Android进阶之路Android进阶之路

概述

先看一个Android中的HandlerThread是如何使用Looper的。

代码语言:txt
复制
public class HandlerThread extends Thread {
    @Override
    public void run() {
    	......
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        ......
        Looper.loop();
        ......
    }

    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
}

在线程的run方法中调用Looper的prepare()方法进行准备工作,准备之后就可以通过Looper.myLooper获取到当前的线程的Looper了。使用然后调用loop方法进入循环处理。使用的时候非常简单。接下来分析Looper的实现。

而且可以quit来退出线程,quit方法中获取当前线程的looper,不为null会调用looper对象quit方法退出。

Looper

prepare方法

先分析一下静态的方法prepare。

代码语言:txt
复制
public final class Looper  {
    ······
    public static void prepare() {
        prepare(true);
    }
    ······
}

对外的静态方法prepare调用私用的带有参数的prepare方法。

代码语言:txt
复制
public final class 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));
    }
    ······
}

参数quitAllowed标识是否允许looper退出。

首先调用sThreadLocal参数获取是否有Looper。

代码语言:txt
复制
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

sThreadLocal线程的本地对象,用于保存线程相关的变量。如果从sThreadLocal获取Looper对象不为null,说明线程已经绑定了Looper,直接抛出异常。如果为从sThreadLocal获取的Looper对象为null,就创建一个Looper,并设置到sThreadLocal中。

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

Looper的构造方法中创建MessageQueue,用于保存消息处理。并保存当前的线程到mThread变量中。

总结一下:

  • Looper.prepare()会调用私有带有参数的prepare方法 -在私有的带有参数的prepare方法中会创建Looper对象,并添加到线程的本地对象中。 -Looper的构造方法中会创建MessageQueue消息队列对象。loop方法public final class Looper { ....... 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; ······ } ....... public static @Nullable Looper myLooper() { return sThreadLocal.get(); } ...... }loop静态方法中,首先调用myLooper获取当前的线程的looper。如果为null,说明线程未绑定Looper,直接抛出异常。如果不为null,获取Looper中的消息队列MessageQueue。public final class Looper { ....... public static void loop() { ······ for (;;) { Message msg = queue.next(); // 消息队列为空,将一直阻塞 if (msg == null) { return; } ······ } ····· } }接着是死循环,一直从消息队列中获取消息message。当msg为null说明MessageQueue正在退出,这里就直接从死循环中退出。接着分析。public final class Looper { public static void loop() { for (;;) { ······ try { msg.target.dispatchMessage(msg); } finally { } ······ } ······ } }消息对象的target对象实际为Handler对象。也就是调用了Handler的dispatchMessage对象来处理消息。public final class Looper { ······ public static void loop() { ······ for (;;) { ······ msg.recycleUnchecked(); } ······ } ······ }最后是调用msg的recycleUnchecked。也是就回收msg,以备下次使用。

总结一下Looper.loop()方法:

-获取当前线程绑定的Looper的消息队列。

-死循环中不断从消息队列中取出Message来处理。有消息就调用消息的target(Handler对象)的来处理。

-最后回收Message。

quit方法

代码语言:txt
复制
public final class Looper  {
    ······
	public void quit() {
        mQueue.quit(false);
    }
    ······
}

Looper的quit方法会调用消息队列的quit方法。

分段阅读MessageQueue的quit方法。

代码语言:txt
复制
public final class MessageQueue {
    ······
	void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            }
            ······
        }
    }
    ······
}

MessageQueue方法中先检测mQuitAllowed用来判断是否支持退出,主线程的looper是不支持的。

检测mQuitting值来判断MessageQueue是否已经退出了。如果mQuitting为true,说明MessageQueue已经退出,就直接返回了。

如果mQuitting是false,接着执行,设置mQuitting = true,接着safe为true,说明是安全退出,会调用 removeAllFutureMessagesLocked()。我们来分析一下removeAllFutureMessagesLocked()

代码语言:txt
复制
private void removeAllFutureMessagesLocked() {
        final long now = SystemClock.uptimeMillis();
        Message p = mMessages;
        if (p != null) {
            //比较当前的message的执行的时间是否大于当前的时间。如果是就直接的移除所有的消息。
            if (p.when > now) {
                removeAllMessagesLocked();
            } else {
                Message n;
                //循环比较获取时间点大于当前的时间点的消息。
                for (;;) {
                    n = p.next;
                    if (n == null) {
                        return;
                    }
                    if (n.when > now) {
                        break;
                    }
                    p = n;
                }
                p.next = null;
                //循环删除消息队列中时间点大于当前时间点的消息。
                do {
                    p = n;
                    n = p.next;
                    p.recycleUnchecked();
                } while (n != null);
            }
        }
    }

此方法主要是根据删除执行的时间点大于当前时间点的消息。我接着分析MessageQueue的quit方法。

代码语言:txt
复制
void quit(boolean safe) {
	······
    synchronized (this) {
      	······
        if (safe) {
            removeAllFutureMessagesLocked();
        } else {
            removeAllMessagesLocked();
        }
        
        nativeWake(mPtr);
    }
}

如果safe为false,不是安全退出,直接调用removeAllMessagesLocked()删除回收所有的Message。

总结一下Looper的quit方法:

-Looper的quit方法会调用消息队列MessageQueue的quit方法

-MessageQueue的quit方法中,如果是非安全退出,直接移除所有的消息。如果是安全退出直接移除执行时间点大于当前时间点的Message。

主线程中的Looper

主线程中的消息的管理也是通过Looper来实现的。它与普通线程的Looper不同的是有特殊的方法,但原理基本一致。

代码语言:txt
复制
public final class Looper {
	private static Looper sMainLooper;  // guarded by Looper.class
	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 Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }
}

-主线程的Looper有单独的变量sMainLooper保存。

-prepareMainLooper方法用于准备主线的Looper,它是在ActivityThread的main方法中调用的,也就是创建主线程时就会创建了。

-getMainLooper方法获取主线程的Looper。

Handler

A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue.

从描述可以总结出Handler一些特性:

1.每个Handler实例都会与一个线程以及线程的messageQueue关联。

2.发送消息。

3.处理消息,处理Runnable。

Handler与Looper的关系图:

Handler处理机制
Handler处理机制

从上面的特性展开了三个问题:

1.Handler是如何与线程的Looper关联的?

2.Handler是如何发送消息的,发送到哪里了?

3.Handler是如何处理消息的?

我们一个一个分析。

Handler与Looper关联

Handler关联Looper其实就是在构造方法中。以默认函数为例。

代码语言:txt
复制
public class Handler {
	public Handler() {
        this(null, false);
    }
    /**
     * @hide
     */
    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;
    }
}

不传递任何参数将调用一个其他的构造方法。构造方法中直接获取Looper.myLooper()来获取当前线程的Looper,保存到mLooper中,然后获取mLooper的消息队列mQueue保存到mQueue中。保存Callback用于处理消息。最后一个是mAsynchronous,标识消息是否为异步处理。

这样就关联到了Looper。

Hanlder处理消息

我们在Looper的loop方法中分析到,处理消息时回调用Message的target的dispatchMessage方法处理,Message的target对象就是Handler对象。

代码语言:txt
复制
msg.target.dispatchMessage(msg);

那我们来分析dispatchMessage来看看如何处理消息的。

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

-如果msg的callback不为null,就是直接调用callback的run方法。一般是通过post方法发送的Runnable。

-然后判断Handler的mCallback是否为null,不为null就调用mCallback的handleMessage的方法处理。

-最后调用Handler的handleMessage来执行。

Handler发送消息

发送消息通过两种方式:

-post相关的方法,来发送Runnable。

-sendMessage相关的方法,发送一个Message。

sendMessage方法

我们先来分析sendMessage方法

代码语言:txt
复制
public class Handler {
    ······
	public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    ······
}

此方法会调用sendMessageDelayed方法,延迟的时间为0。

代码语言:txt
复制
public class Handler {  
    ······
	public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    ······
}

此方法又会委托给sendMessageAtTime来发送消息。

代码语言:txt
复制
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);
}

此方法最后会调用enqueueMessage来完成发送消息。

代码语言:txt
复制
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

设置msg的target对象为当前对象,用于处理msg。

并调用消息队列MessageQueue的enqueueMessage来添加到队列中去。

总结:

-sendMessage的方法最终会调用sendMessageAtTime方法,然后调用enqueueMessage()方法,而在enqueueMessage()方法中会设置message.target为此Handler,这样Looper获取到的Message可以调用message.target来处理了。

post方法

我们来分析post相关方法。

代码语言:txt
复制
public class Handler {  
    ······
	public final boolean post(Runnable r)
	{
   		return  sendMessageDelayed(getPostMessage(r), 0);
	}
    ······
}

post方法显示通过getPostMessage获取一个Message。

代码语言:txt
复制
public class Handler {  
    ······
	private static Message getPostMessage(Runnable r) {
    	Message m = Message.obtain();
    	m.callback = r;
    	return m;
	}
    ······
}

然后通过sendMessageDelayed发送消息。

总结:

-post方法实际上就是封装成Message再发送。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
    • Looper
      • prepare方法
        • quit方法
          • 主线程中的Looper
            • Handler
              • Handler与Looper关联
                • Hanlder处理消息
                  • Handler发送消息
                    • sendMessage方法
                      • 总结:
                    • post方法
                      • 总结:
                      相关产品与服务
                      消息队列 CMQ 版
                      消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档