【 Andoird 】 Hanlder 原理分析 + 源码执行流程(详细易懂)

425 阅读5分钟

为什么存在 Handler

Handler 是 Android 的一种消息处理机制,与 Looper,MessageQueue 绑定。可用于不同线程的通信,常见于在子线程发送消息让主线程(UI 线程)更新控件。

Hanlder 的基本原理

在 Android 的多线程中,每个线程都有一个自己的消息队列,线程可以开启一个死循环不断地从队列中读取消息。

当 B 线程要和 A 线程通信时,只需要往 A 的消息队列中发送消息,A 的事件循环就会读取这一消息从而实现线程间通信

Handler 源码分析

默认整个应用程序,都是通过ActivityThread类启动的,在ActivityThread类当中,负责创建我们所有的Activity,并回调每个Activity中的生命周期方法。在ActivityThread类中,默认会去创建一个线程,这个线程叫做main线程(主线程)。所有的应用程序,更新ui的操作,都是在这个main线程中进行的。

#ActivityThread 类
public static void main(String[] args) {
   
   ...
    //省略了其他 主要看与 Hanlder 有关的方法
    Looper.prepareMainLooper();
   ...
    Looper.loop();
 
   
}
#Looper 类
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

跟进去prepare方法 ⬇️

#Looper 类
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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));
}

ThreadLocal 原理

目前线程:main 线程(即 Android 中绘制 UI 的线程)

在当前方法中,会new 一个 Looper 实例对象存在 ThreadLocal 中。

我们接着看看 new Looper 发生了什么:

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

我们看到,在创建Looper轮训器的时候,同时创建了消息队列MessageQuene并存于 Looper 实例对象的变量中。也就是说默认的情况下,android为我们自动创建了主线程的Looper和MessageQuene。

这些都是系统自动为我们做的,而在开发中要做的,是为线程实例化一个 Handler 对象:

##MainActivity 类

private Handler handler = new Handler() {
    @Override
    public void handleMessage(@NonNull Message msg) {
       switch (msg.what) {
       //自己开发中用到的逻辑
           case xx:
           break;
           
           default:
           break;
       }
    }
};

那么问题来了,Handler怎么和我们的Looper 、MessageQuene消息队列联系在一起的?因为不是说handler发出的消息是发送到消息队列中了吗?

借着问题,我们来看看 new Handler 发生了什么?

#Handler 类
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;
    mCallback = callback;
    mAsynchronous = async;
}

在执行mLooper = Looper.myLooper()时:由于目前 new Handler 是在 main 线程中,从 Looper 的 sThreadLocal 中获取到的 Looper 实例,正是系统为我们自动创建的那个。至此,在 Handler 中的成员变量 mLooper,指向的就是系统创建的 Looper 了,所以两者到此关联了起来。

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

插一张关系图:

image.png

好,到此为止,Handler 的准备工作已经完成了,下面研究一下子线程发送消息到main 线程,消息是如何处理的。

#MainActivity类
    @Override
    public void onClick(View v) {
        new Thread(new Runnable() {
            @Override
            public void run(){
                Message message = new Message();
                message.what = UPDATE;
                handler.sendMessage(message);
            }
          }
        ).start();
    
    }

如上面模拟了在 activity 中,开启子线程使用 handler 发送消息。最终会发现,执行到了sendMessageAtTime方法。

#Handler 类
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);
}

继续跟进enqueueMessage方法:(顾名思义就是将消息放到 handler 实例中的 MessageQueue 中。

#Hanlder 类
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);
}

MessageQueue实际上是一条链表,queue.enqueueMessage将消息 add 进链表中,等待Looper轮询。

前面提到,在 ActivityThread 中,prepare 完了之后会执行Looper.loop();这个方法就是 Looper 开启死循环执行轮询的方法。我们跟进看看:

#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.");
    }
   
   ...

    for (;;) {
        if (!loopOnce(me, ident, thresholdOverride)) {
            return;
        }
    }
}

for 循环就是死循环轮询的具体执行:

#Looper类,省略了很多源码
private static boolean loopOnce(final Looper me,
        final long ident, final int thresholdOverride) {
    Message msg = me.mQueue.next(); // might block
     ...
    try {
    //关键在这里
        msg.target.dispatchMessage(msg);
        if (observer != null) {
            observer.messageDispatched(token, msg);
        }
        dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
    } catch (Exception exception) {
        if (observer != null) {
            observer.dispatchingThrewException(token, msg, exception);
        }
        throw exception;
    } finally {
        ThreadLocalWorkSource.restore(origWorkSource);
        if (traceTag != 0) {
            Trace.traceEnd(traceTag);
        }
    }
   ...
}

msg.target.dispatchMessage(msg);关键在这句代码中。

msg.targer会得到 handler 实例对象,再调用其dispatchMessage方法。

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

一般情况下我们发送消息的时候没有给Message的callback赋值,所以第一个if条件不满足。下面的mCallback是在我们初始化Handler的时候才被初始化,Handler初始化有一种方法Handler(Callback callback),此处的参数就是给mCallback赋值的。我们一般初始化Handler的时候使用的是空参数的构造器,所以导致mCallback未被初始化,所以会直接走handleMessage(msg)方法,也就是我们初始化Handler时重写的handleMessage方法。到此为止,已经分析完了!

相关问题

  1. 在死循环的过程中,不会卡死主线程吗?

可参考#  为什么Looper中的Loop()方法不能导致主线程卡死?

  1. Looper是线程唯一的吗?

参考:# 一切从Android的Handler讲起(三):Looper的唯一性——ThreadLocal

从 Looper 的源码出发,一个线程创建 Looper是源于 Looper.prepare()方法:

# Looper类
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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)); 
}

其他的就是 ThreadLocal 的原理啦!

  1. 一个线程能有几个 Handler?

无数个。

理论上只要你内存足够,你可以一直 new 。

一个线程可以有多个Handler, 但是只有一个Looper对象,只有一个MessageQueue对象。

所以需要用到 ThreadLocal 来保证一个线程只有一个 Looper 。

(在学习的时候一直有一个问题困扰着,就是为什么要使用 ThreadLocal 呢, 如果要保证一个线程只有一个 Looper,那么直接在线程的实例对象中存一个 looper 的成员变量不就好了吗?想要用到该线程的 looper 时,直接去获取线程中的成员变量就好了。这样也可以保证一个线程只有一个 Looper?) 有没有大神解答一下。

同步屏障