欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android消息机制系列(1)——Handler Looper Message源码解析

程序员文章站 2022-07-14 16:45:28
...

一、基本概念:

在谈到Android消息机制的时候,我们不可避免的要涉及要一下几个概念:
1、Message 消息
2、MessageQueue 消息队列
3、Handler 处理者?它的实际作用包括发送消息(sendMessage),处理消息(handleMessage)
4、Looper 消息循环,或者轮询器

从字面上来看,前两者都比较好理解。剩下的Handler、Looper,让我们先重点理解一下Looper:

二、Looper

Looper类的作用:
Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.

简单翻译过来就是:Looper用于管理一个线程的消息循环。 默认情况下,线程是没有相关联的Looper的,需要调用Looper.prepare()来创建,创建完成之后,Looper.loop()会不断地循环来处理消息,直到循环停止。一般情况下是和Handler来配合使用的。

使用Looper+Handler典型代码例如:

class LooperThread extends Thread {
    public Handler mHandler;
    public void run() {
        Looper.prepare(); //为本线程创建一个Looper
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
            // process incoming messages here
            }
        };
        Looper.loop(); //消息循环
    }
}

Looper重要属性:

// sThreadLocal.get() will return null unless you've called prepare()
static final ThreadLocal sThreadLocal = new ThreadLocal();
private static Looper sMainLooper;  // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;

其中,sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。

Looper的prepare()方法:

public static void prepare() {
    prepare(true);
}
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) { //一个线程只能对应一个Looper实例
    throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed)); //new一个Looper的实例保存在ThreadLocal中
}

可以看到,prepare()方法将new一个Looper的实例,同时将该Looper实例放入了ThreadLocal。并且当第二次调用时报错,说明prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例。

Looper的构造函数:

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

Looper构造函数中创建一个消息队列MessageQueue。同时也可以看到,一个Looper实例对应一个线程、一个MessageQueue

Looper的loop()方法:

public static void loop() {
    // myLooper() 返回的是sThreadLocal.get(),也就是sThreadLocal存储的Looper实例
    final Looper me = myLooper(); 
    if (me == null) {
        // 可以看到这里,如果在线程中没有调用Looper.prepare()的话Looper实例是空的
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on    this thread.");
    }
    final MessageQueue queue = me.mQueue; //拿到Looper实例对应的消息队列
    // 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;
    }
    ... ...
    msg.target.dispatchMessage(msg); //真正处理消息的地方
    ... ...
    // 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();
    }
}

可以看到,方法中无限循环体里面,不断地通过queue.next()来取出准备处理的消息。而真正处理消息的地方是在这一句:msg.target.dispatchMessage(msg)。target是什么?我们可以从Message源码中可以看到,target是一个Handler,也就是说,真正处理消息是该消息对应的Handler实例的dispatchMessage(msg)方法中。

小结:
1、Looper用于管理一个线程的消息循环。
2、由于默认情况下,线程是没有相关联的Looper的,因此必须调用Looper.prepare()来创建。
3、Looper.prepare()方法,会使得线程绑定唯一一个Looper实例
4、Looper创建时也会创建一个消息队列MessageQueue。
2、Looper.loop()方法,不断从MessageQueue.next()中去取出准备处理的消息,交给消息的对应的handler的dispatchMessage去处理。

简言之,Looper提供了存储消息的队列(MessageQueue),同时不断循环取出准备处理的消息。那么谁来发送消息和真正处理消息呢?——当然是我们熟悉的Handler了。

三、Handler:

从第一部分我们可以推测出,Handler的两个重要作用:
1、发消息到MessageQueue中
2、接受Looper分发的消息处理任务,真正处理消息

那么Handler是怎么和一个线程的MessageQueue、Looper实例关联上的呢?

这个要看一下Handler是怎样被new出来的。具体我会另开文分析,这里以开篇的例子来分析,当使用new Handler()来创建一个Handler的情况(意思是还有其他情况):

public Handler() {
    this(null, false);
}

最后是调用了这个:

  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();  //这里,关联上了当前线程的Looper实例
    if (mLooper == null) {
        throw new RuntimeException( 
        // 在一个线程里面,创建handler之前必须调用Looper.prepare()
        "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue; // 这里,取得当前线程的Looper实例的消息队列
    mCallback = callback;
    mAsynchronous = async;
}

可以看到,在这种情况下,在构造函数里面,通过Looper.myLooper()获取了当前线程保存的Looper实例,然后又获取了这个Looper实例中保存的消息队列MessageQueue。Handler实例就是这样和一个线程的MessageQueue、Looper实例一一关联上的。

这里的当前线程,是指创建Handler实例所使用的线程。

接下来就简单了,用Handler发送一条消息,不论使用何种方法,最终都会调到这个函数:

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue; //当前线程对应Looper实例的消息队列
    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);
}

然后调用了这个函数:

private boolean enqueueMessage(MessageQueue queue, Message msg,long     uptimeMillis) {
    msg.target =this; //将消息的target设为当前的Handler实例
    if(mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis); //将消息插入到消息队列中去
}

重要的是这句:msg.target = this; //将消息的target设为当前的Handler实例

还记得第一部分结尾处,loop()方法中取出一条要处理的消息,然后调用msg.target.dispatchMessage(msg)嘛?那么现在我们知道了,msg的target就是在发这条消息的时候设置上的。以保证后续处理消息的时候,能找到处理这条消息的Handler。

queue.enqueueMessage(msg, uptimeMillis); //这句将设置好target等属性的消息放到消息队列中去

终于,让我们看一下msg.target.dispatchMessage(msg)的dispatchMessage()函数:

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

public void handleMessage(Message msg) {
}

这个方法最终调用我们在创建Handler时重写的handleMessage()方法或者callback来进行消息处理;

而handleMessage()我们就很熟悉了,每个创建Handler的都要重写handleMessage()方法,根据msg.what来处理消息,例如:

private Handler mHandler = newHandler() {
    public void handleMessage(android.os.Message msg) {
        switch(msg.what) {
            case:
            break;
            default:
            break;
        }
     };
};

三、总结

1、默认情况下线程是没有Looper实例的,Looper.prepare()为线程关联到唯一的Looper实例,以及Looper实例的MessageQueue。

2、Handler被创建时,会关联到“某个线程”的唯一的Looper实例和MessageQueue;Handler的主要作用是将处理消息切换到“这个线程”来处理。

3、当通过Handler发送一条Message时,该消息的target就被设为这个Handler实例(this)

4、Handler发送一条消息,实际上是将这条消息插入到对应的消息队列MessageQueue中

5、线程对应的Looper实例loop()方法来处理消息时,会根据这条消息的target(也就是Handler实例),回调Handler的dispatchMessage()方法进行处理。dispatchMessage()方法最终调用我们在创建Handler时重写的handleMessage()方法或者callback。

这里面,比较重要的是,明确Handler被创建时,和哪个线程或者和哪个线程的Looper相关联,这将决定了任务最后在哪个线程执行;

这里面有个特殊情况,就是在主线程创建Handler时不需要调用prepare()和loop(),因为这部分工作Android已经帮我们做过了,具体可以看一下ActivityThread的代码。

简单的说,一条消息从发出到处理经历了:

[Handler发送消息] -> [消息进入Looper的MessageQueue队列] -> [loop循环从MessageQueue取出要处理的消息] -> [Handler处理消息]


Reference:

Handler源代码

Looper源代码

Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系

Android异步消息处理机制完全解析,带你从源码的角度彻底理解

Android的消息机制之ThreadLocal的工作原理

Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

Android异步消息处理机制详解及源码分析

Handler常见用法