Android-Handler源码解析-Looper

  • Post author:
  • Post category:其他


成员变量

// Log的TAG
private static final String TAG = "Looper";

// 线程本地变量,保证了每个线程仅有唯一的Looper对象。
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// 主线程的Looper,由ActivityThread的main方法内调用Looper.prepareMainLooper()进行创建。
@UnsupportedAppUsage
private static Looper sMainLooper;
// 消息分发状态的观察者,有分发开始、分发异常、分发结束。
private static Observer sObserver;

// 消息队列
@UnsupportedAppUsage
final MessageQueue mQueue;
// 创建Looper的线程
final Thread mThread;
// 是否在loop中
private boolean mInLoop;

// 日志打印类
@UnsupportedAppUsage
private Printer mLogging;
// Trace追踪标记
private long mTraceTag;

// 慢分发阈值时间,如果设置了这个参数,如果消息分发的时间超过这个值,则这个looper将显示一个警告日志。
private long mSlowDispatchThresholdMs;
// 慢交付阈值时间,如果设置了这个参数,如果消息执行(实际交付时刻dispatchStart - post时刻msg.when)的时间超过这个值,则这个looper将显示一个警告日志。
private long mSlowDeliveryThresholdMs;
// 是否慢交付检测,如果消息交付时间超过mSlowDeliveryThresholdMs,则为true。
private boolean mSlowDeliveryDetected;
复制代码

说明:


  1. Looper

    为什么需要持有

    MessageQueue

    ,因为

    Looper


    轮询

    的时候需要知道轮询哪个

    MessageQueue

创建Looper

想要

使用


Looper

,首先要

创建


Looper

,所以我们接下来看下它是如何被

创建

的。

Looper.prepare()

public static void prepare() {
    prepare(true);
}

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
	// 调用线程已经有Looper了,再进行prepare,则抛出异常(每个线程只能创建一个Looper)。
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    // 创建Looper,并存入到sThreadLocal中。
    sThreadLocal.set(new Looper(quitAllowed));
}
复制代码


prepare()

方法,为

静态

方法,为

调用线程

创建一个

Looper



存入



sThreadLocal

中,以便后续

获取此线程



Looper



quitAllowed

参数传入为

ture

,以

允许退出


Looper

说明:


  1. 一个线程

    ,只能

    调用一次


    prepare()



    prepare(boolean)

    方法,否则

    抛出异常


  2. 一个线程

    ,只能

    有一个


    Looper


  3. prepare()

    方法创建的

    Looper



    允许退出

接下来我们来看一下

Looper


构造方法

private Looper(boolean quitAllowed) {
    // 创建消息队列
    mQueue = new MessageQueue(quitAllowed);
    // 记录创建线程
    mThread = Thread.currentThread();
}
复制代码


Looper


构造方法

,为

私有

方法,

不能

通过

new

创建,参数

quitAllowed



是否允许退出

,创建

Looper

会创建一个

MessageQueue

,并记录

创建线程

说明:


  1. 一个


    Looper



    只有一个


    MessageQueue



    只有一个关联


    Thread

Looper.prepareMainLooper()

@Deprecated
public static void prepareMainLooper() {
    // 准备Looper,如没有则进行创建,不允许退出。
    prepare(false);
    // 同步,保证线程安全。
    synchronized (Looper.class) {
        if (sMainLooper != null) {
	    // 已经创建MainLooper了,再准备,则抛出异常。
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        // 获取调用线程的Looper,为MainLooper。
        sMainLooper = myLooper();
    }
}
复制代码


prepareMainLooper()

方法,为

静态

方法,为

主线程

准备

Looper

,并传入

quitAllowed



false

使其

不允许退出

,并将其

标记



app






looper



myLooper()

相关介绍,看后面的-

获取Looper


已被声明



@Deprecated

(弃用),因为

app



主线程


looper

是由

Android

环境

创建

的(

ActivityThread



main

方法内调用

Looper.prepareMainLooper()

创建),因此永远

不需要

自己

调用

这个函数。

说明:


  1. 多个线程

    只能

    调用一次


    prepareMainLooper()

    方法,否则抛出异常。

  2. 主线程


    Looper

    ,是由

    Android

    环境

    创建

    的(

    ActivityThread



    main

    方法),不需要我们关心。

  3. 主线程


    Looper



    不允许退出

小结


  1. 创建



    非主线程


    Looper



    只能

    通过

    Looper.prepare()

    方法

    创建

    ,它是

    允许退出

    的。

  2. 创建



    主线程


    Looper

    ,它是通过

    Android

    环境

    创建



    ActivityThread



    main

    方法内调用

    Looper.prepareMainLooper()

    创建),它是

    不允许退出

    的。
  3. 一个

    线程

    ,一个

    Looper

    ,一个

    MessageQueue

获取Looper


创建完


Looper

后,便可以

获取


Looper


使用

了,所以我们接下来看下它是如何被

创建

的。

Looper.myLooper()

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
复制代码


myLooper()

方法,为

静态

方法,返回与

当前线程

关联的

Looper

对象。如果

调用线程没有



Looper

关联,则返回

null

Looper.getMainLooper()

public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}
复制代码


getMainLooper()

方法,为

静态

方法,返回

主线程


Looper

对象。

小结


  1. 获取


    Looper

    ,有两个方法:

    Looper.myLooper()



    Looper.getMainLooper()


  2. Looper.myLooper()

    方法,获取

    调用线程



    Looper

    ,有可能为

    null


  3. Looper.getMainLooper()

    方法,获取

    主线程



    Looper

    ,不会为

    null

轮询Looper



Looper

创建好后,需要调用

Looper.loop()

方法,使其进入

消息循环

中,所以我们接下来看下它的

Looper.loop()

方法。

Looper.loop()

public static void loop() {
    // 获取当前线程Looper对象
    final Looper me = myLooper();
    if (me == null) {
	// Looper对象为空,抛出异常(没有Looper; 这个线程上没有调用Looper.prepare())。
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    if (me.mInLoop) {
	// 已经在loop中,再调用loop(),则提示警告(再次loop将使队列中的消息在此消息完成之前被执行)。
        Slog.w(TAG, "Loop again would have the queued messages be executed"
                + " before this one completed.");
    }
    // 标记在loop中
    me.mInLoop = true;

    // 确保这个线程的标识是本地进程的标识,并跟踪标识token实际上是什么。
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    // 允许用系统道具(adb命令)覆盖一个阈值,例如:
    // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
    final int thresholdOverride =
            SystemProperties.getInt("log.looper."
                    + Process.myUid() + "."
                    + Thread.currentThread().getName()
                    + ".slow", 0);
    // 慢交付检测,恢复默认值。
    me.mSlowDeliveryDetected = false;

    // 死循环,遍历消息队列里的消息。
    for (;;) {
	// 调用loopOnce方法,进行处理。
        if (!loopOnce(me, ident, thresholdOverride)) {
	    // loopOnce方法返回false,退出循环,结束loop。
            return;
        }
    }
}
复制代码


loop()

方法,为

静态

方法,内部执行

死循环

进行

轮询

,调用

loopOnce()

方法

轮询单个消息



loopOnce()

方法返回

true

,则继续

轮询下一个消息



返回


false

,则

退出循环



结束


loop

说明:


  1. 当前线程

    调用

    Looper.loop()

    方法前,

    一定

    要为

    当前线程

    准备

    Looper

    (调用

    Looper.prepare()

    ),否则

    抛出异常

接下来,我们来看一下

loopOnce()

方法。

private static boolean loopOnce(final Looper me,
        final long ident, final int thresholdOverride) {
    // 获取此Looper的消息队列里面的下个消息,有可能会阻塞线程。
    Message msg = me.mQueue.next();
    if (msg == null) {
        // 没有消息,表示消息队列正在退出(调用quit()方法),直接返回false以退出loop。
        return false;
    }

    // 获取日志打印类,这必须在一个局部变量中,以防UI事件设置logger。
    final Printer logging = me.mLogging;
    if (logging != null) {
	    // 打印日志:Dispatching(分发中)
        logging.println(">>>>> Dispatching to " + msg.target + " "
                + msg.callback + ": " + msg.what);
    }

    // 获取Looper的全局观察者,使用变量,确保观察者在处理事务时不会更改。
    final Observer observer = sObserver;

    // 获取追踪标记
    final long traceTag = me.mTraceTag;
    // 获取消息分发阈值
    long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
    // 获取消息交付阈值
    long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
    if (thresholdOverride > 0) {
	// 有覆盖的(adb命令传入),则用覆盖的值。
        slowDispatchThresholdMs = thresholdOverride;
        slowDeliveryThresholdMs = thresholdOverride;
    }
    // 是否记录慢交付
    final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
    // 是否记录慢分发
    final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

    // 是否需要获取开始时刻
    final boolean needStartTime = logSlowDelivery || logSlowDispatch;
    // 是否需要获取结束时刻
    final boolean needEndTime = logSlowDispatch;

    if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
	// 开始追踪
        Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
    }
    
    // 分发开始时刻
    final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
    // 分发结束时刻
    final long dispatchEnd;
    // token,用于Observer回调间传递。
    Object token = null;
    if (observer != null) {
	// 获取token,通知Observer消息分发开始。
        token = observer.messageDispatchStarting();
    }
    // 设置当前线程的Uid
    long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
    try {
	// 通知Message的Handler进行分发消息(重要!!!)
        msg.target.dispatchMessage(msg);
        if (observer != null) {
	    // 通知Observer消息分发结束
            observer.messageDispatched(token, msg);
        }
        // 记录分发结束时刻
        dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
    } catch (Exception exception) {
        if (observer != null) {
  	    // 通知Observer消息分发异常
            observer.dispatchingThrewException(token, msg, exception);
        }
        throw exception;
    } finally {
	// 恢复当前线程的Uid
        ThreadLocalWorkSource.restore(origWorkSource);
        if (traceTag != 0) {
            // 结束追踪
            Trace.traceEnd(traceTag);
        }
    }
    if (logSlowDelivery) {
        // 记录慢交付,如果(dispatchStart - msg.when)时间大于阈值,则进行Slog提示警告。
        if (me.mSlowDeliveryDetected) {
            if ((dispatchStart - msg.when) <= 10) {
                Slog.w(TAG, "Drained");
                me.mSlowDeliveryDetected = false;
            }
        } else {
            if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                    msg)) {
                me.mSlowDeliveryDetected = true;
            }
        }
    }
    if (logSlowDispatch) {
        // 记录慢分发,如果(dispatchEnd - dispatchStart)时间大于阈值,则进行Slog提示警告。
        showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
    }

    if (logging != null) {
  	// 打印日志:Finished(已完成)
        logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
    }

    // 确保在分发过程中线程的标识没有损坏。
    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();

    return true;
}
复制代码


loopOnce()

方法,为

静态

方法,为

轮询单个消息

。它会先获取

消息队列



下个


Message

,然后调用

Message



目标


Handler

进行

分发

说明:



  1. Looper



    MessageQueue

    里面

    没有

    消息时,则线程

    进入阻塞

    ,释放

    CPU

    ,它

    不会因为没有数据而停止


    loop()

    ,所以如果此

    Looper


    不使用了



    一定要

    调用

    quit()



    结束循环

    ,它会使

    MessageQueue



    next()

    方法返回

    null

    ,从而

    结束循环

  2. 可通过调用

    looper.setMessageLogging()



    指定


    Looper

    设置日志打印者,以

    监听消息状态



    >>>>> Dispatching to



    分发开始



    <<<<< Finished to



    分发结束

    ,可判断

    时间差

    来判断

    分发是否超时

小结


  1. 轮询


    Looper

    前,

    调用

    线程

    一定

    要有

    Looper

    (需调用

    Looper.prepare()

    ),否则

    抛出异常


  2. 非主线程


    Looper

    ,如果不使用了,

    一定要

    调用

    quit()



    结束循环



  3. MessageQueue

    里面

    没有

    消息时,则线程

    进入阻塞

    ,释放

    CPU

    ,它

    不会因为没有数据而停止


    loop()

    ,所以如果此

    Looper


    不使用了



    一定要

    调用

    quit()



    结束循环

退出Looper


Looper


不使用了

,便需要

退出

,所以我们接下来看下它是如何

退出

的。

quit()

public void quit() {
    mQueue.quit(false);
}
复制代码


quit()

方法,为

不安全



退出


Looper

说明:

  1. 此方法,导致

    loop()

    方法,在

    不处理

    消息队列中的

    任何消息

    的情况下

    终止

    ,因为它会

    删除消息队列



    所有消息



  2. looper

    被请求

    退出

    后,

    任何

    向队列

    发送消息

    的尝试

    都将失败

    。例如,

    Handler.sendMessage(Message)

    方法将返回

    false

  3. 此方法,

    可能



    不安全

    的,因为在

    looper


    终止之前

    可能

    无法传递

    一些消息。考虑使用

    quitSafely

    ,以

    确保所有等待的工作

    都以有序的方式

    完成

quitSafely()

public void quitSafely() {
    mQueue.quit(true);
}
复制代码


quitSafely()

方法,为

安全



退出


Looper

说明:

  1. 此方法,导致

    loop()

    方法,在

    处理

    消息队列中的

    已经到期



    所有消息



    不处理

    消息队列中的

    未来(当前时刻以后)到期



    所有消息

    的情况下

    终止

    ,因为它会

    删除消息队列



    所有未来的消息

小结


  1. 退出


    Looper

    ,有两个方法:

    quit()

    不安全退出、

    quitSafely()

    安全退出。

  2. quit()

    方法,在

    不处理

    消息队列中的

    任何消息

    的情况下

    终止

    (此方法,

    可能



    不安全

    的,因为在

    looper


    终止之前

    可能

    无法传递

    一些消息)。

  3. quitSafely()

    方法,在

    处理

    消息队列中的

    已经到期



    所有消息



    不处理

    消息队列中的

    未来(当前时刻以后)到期



    所有消息

    的情况下

    终止



  4. looper

    被请求

    退出

    后,

    任何

    向队列

    发送消息

    的尝试

    都将失败

    。例如,

    Handler.sendMessage(Message)

    方法将返回

    false

属性set、get方法


Looper



mLogging



mThread



mQueue

属性对外提供了

set



get

方法,我们接下来看下它们的实现。

setMessageLogging()

public void setMessageLogging(@Nullable Printer printer) {
    mLogging = printer;
}
复制代码


设置



Looper


处理消息



控制消息



日志记录

。如果

启用



日志消息

将在

每次

消息

分发



开始



结束

时写到

printer



标识

目标

Handler

和消息内容。

getThread()

public @NonNull Thread getThread() {
    return mThread;
}
复制代码


获取

与此

looper


关联



线程

getQueue()

public @NonNull MessageQueue getQueue() {
    return mQueue;
}
复制代码


获取



looper



消息队列

其它

Looper.myQueue()

public static @NonNull MessageQueue myQueue() {
    return myLooper().mQueue;
}
复制代码


获取



当前线程

关联的

MessageQueue

对象。此线程

必须



Looper



否则



抛出


NullPointerException

isCurrentThread()

public boolean isCurrentThread() {
    return Thread.currentThread() == mThread;
}
复制代码

判断

调用线程

是否是该

looper



线程

,如果是,则返回

true

dump()

public void dump(@NonNull Printer pw, @NonNull String prefix) {
    pw.println(prefix + toString());
    mQueue.dump(pw, prefix + "  ", null);
}
复制代码


转储


looper



状态

,以进行

调试

,调用

MessageQueue



dump()

方法。



版权声明:本文为xiaopangcame原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。