Android 从源码角度分析消息处理机制(Handler,Looper,Message)

来源:互联网 发布:linux 统计登录次数 编辑:程序博客网 时间:2024/05/01 05:14

Android 从源码角度分析消息处理机制(Handler,Looper,Message)

前言

在Android中,修改UI的操作必须要放入到主线程中。而我们的网络请求往往是长时操作,需要放入到子线程进行请求。可以通过Handler实现不同线程间的通信。对于如何实现的,网上有很多的教程或博文,也解释的非常清楚,这里不在多叙。

但在使用过程中,我们可能会有一些疑问:

  • 为什么可以通过Handler实现不同的线程间通信。

  • 通过sendXXX()方法,是怎么在handlerMessage()中回调的

  • 为什么主线程不需要Looper.prepare()方法,而子线程需要Looper.prepare()Looper.loop()方法。

可能很多博客都会从Handler,Looper,MessageQueue他们之间的联系上,去解释Android 中的消息处理机制,本篇博客意在从源码的角度分析其通信原理。

源码分析

在通常的编码中,onCreate()方法是一个Activity的入口,我们往往在这里做一些初始化操作。而对于一个程序来说,ActivityThreadmain()方法,便是一个应用的入口,所以我们先看一下main()方法的源码

    public static void main(String[] args) {        // 省略..        // 方法一,构造Looper 对象。        Looper.prepareMainLooper();        ActivityThread thread = new ActivityThread();        thread.attach(false);        if (sMainThreadHandler == null) {            sMainThreadHandler = thread.getHandler();        }        // 省略...        // 方法二,开始消息队列的循环        Looper.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }

main()方法中,存在两个关键性的方法,分别是方法一和方法二。从英文命名上,我们可以大致猜出他们的意思,分别是准备主线程的Looper对象和开始循环消息队列。

进入到Looper.prepareMainLooper()方法中,

   public static void prepareMainLooper() {        // 构造Looper 方法        prepare(false);        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            // 获取当前的Looper()对象,进行赋值到sMainLooper            sMainLooper = myLooper();        }    }

prepare(false)方法构造属于当前线程的Looper对象,传入参数暂且不管,稍后会看其源码。

然后判断sMainLooper对象是否为null,如果不为null就会抛出异常,异常的内容“主线程的Looper对象已经准备”,在这里,通过字段的命名可以看出sMainLooperLooper对象的静态字段,并且保存主线程的Looper对象。

那么从prepareMainLooper()和判断条件可以得出,构造主线程的Looper对象,保存对象到sMainLooper中。如果sMainLooper不为null,表明重复调用,抛出异常。

那么进入到prepare(false)方法中,

    private static void prepare(boolean quitAllowed) {        // 判断是否创建过Looper 对象        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        // 创建Looper 对象,并保存        sThreadLocal.set(new Looper(quitAllowed));    }

先是通过sThreadLocal.get()方法获取当前线程的Looper对象,如果不为null,则抛出异常“一个线程只能创建一个Looper对象。”

如果不为null,则创建Looper对象并保存。

那么sThreadLoacl又是什么呢,从命名上可以看出他是一个静态字段,看一下声明,

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

ThreadLocal类型,该类是java.lang中。看一下他的get()set()方法,因为我们主要是通过这两个方法获取对应的Looper对象。

    // get 方法    public T get() {        // 获取当前所在的线程,很关键        Thread currentThread = Thread.currentThread();        Values values = values(currentThread);        if (values != null) {            Object[] table = values.table;            int index = hash & values.mask;            if (this.reference == table[index]) {                return (T) table[index + 1];            }        } else {            values = initializeValues(currentThread);        }        return (T) values.getAfterMiss(this);    }    public void set(T value) {        // 获取当前的线程        Thread currentThread = Thread.currentThread();        Values values = values(currentThread);        if (values == null) {            values = initializeValues(currentThread);        }        values.put(this, value);    }

这两个方法,看起来有点晕晕的,我们不深究其细节实现,明白其功能即可。

get()能够根据当前线程返回对应的Looper对象

set()能够根据当前线程保存对应的Looper对象。

继续回到prepare()方法中,我们通过get()判断,通过set()进行保存。其中通过new Looper(quitAllowed)方法,构造Looper对象,看一下他的实现

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

Looper的构造方法中,构造了一个MessageQueue()对象并保存,同时保存当前线程的对象。那么关键词出现了,MessageQueue,这时网上很多分析中出现的关键类,负责消息的队列。由此可以看出,Looper对象中保存有MessageQueue的实例。

在这里,我们需要注意的是mQueuemThread,从命名可以看出都是成员变量,即他和Looper的实例都是一一对应的。

public final class Looper {    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();    private static Looper sMainLooper;  // guarded by Looper.class    // 每一个looper 对应的消息队列    final MessageQueue mQueue;    // looper 对应的线程    final Thread mThread;}

将字段的具体声明贴出,以便理解。

那么,到此,整个Looper.prepareMainLooper()方法就大致分析完了。

Looper.loop()

ActivityThread.main()中,最后调用了Looper.loop()方法,看一下此方法的实现。

public static void loop() {        // 1.获取Looper 对象        final Looper me = myLooper();        if (me == null) {            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");        }        // 2.获取Looper对象对应的消息队列        final MessageQueue queue = me.mQueue;        // 3.无线循环,遍历消息队列        for (;;) {            Message msg = queue.next(); // 4.循环消息队列            if (msg == null) {                // No message indicates that the message queue is quitting.                return;            }            msg.target.dispatchMessage(msg);// 5.分发消息            msg.recycleUnchecked(); // 6.回收资源        }    }

中间省略了一些代码,自上往下分析:

1的地方首先获取到当前线程的Looper对象。

2的时候获取到Looper对象的MessageQueue实例,在创建Looper时,构造方法中创建了和他一一对应的MessageQueue对象,主要是作为消息队列。

然后无限for循环,遍历消息队列。

通过MessageQueuenext()方法获取到Message对象,具体的5和6,我们需要看一下Handler的实现后进行理解。

Handler的实现

通过上面的分析,我们知道在主线程中构造了Looper对象并且开始轮询。那么另一个关键人物Handler,势必要分析他们直接的联系。

看一下Handler的构造方法

 public Handler(Callback callback, boolean async) {        // ...        // 获取当前线程的Looper对象        mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }        // 保存Looper对象的消息队列        mQueue = mLooper.mQueue;        mCallback = callback;        mAsynchronous = async;    }

通常的使用,都是直接使用new Handler(),最终仍会调用此构造方法,默认为null和false。

在这个方法中,handler保存了当前线程的looper的实例和消息队列。

通常的使用中通过handler.sendMessage()向主线程发送消息。看一下实现

 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(queue, msg, uptimeMillis),看一下这个方法,

 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        // msg.target = handler        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

该方法,将当前handler对象赋值到msg.target中,并且调用MessageQueueenqueueMessage()方法。

到此,对Handler的分析就差不多结束了。回过头,在ActivityThread.main()中,Looper.loop()我们还有一点没有分析,就是无限循环的处理,再看一下源码

        // 3.无线循环,遍历消息队列        for (;;) {            Message msg = queue.next(); // 4.循环消息队列            if (msg == null) {                // No message indicates that the message queue is quitting.                return;            }            msg.target.dispatchMessage(msg);// 5.分发消息            msg.recycleUnchecked(); // 6.回收资源        }

我们知道,msg.targer是我们构造的handler,那么绕了一圈有回到了自身的handler上,调用handler.dispatchMessage(msg)方法,看一下其实现,

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

其中由几个判断,最终不通过的时候调用handlerMessage(msg)方法。看一下之前的判断

msg.callback指定的是什么,在什么情况下不等于null呢,在使用handler的时候,还有一种使用方式及handler.post(Runnable r),看一下他的实现

 public final boolean post(Runnable r)    {       return  sendMessageDelayed(getPostMessage(r), 0);    } private static Message getPostMessage(Runnable r) {        Message m = Message.obtain();        // 赋值        m.callback = r;        return m;    }

这两个方法比较清晰,从这个地方可以看出,msg.callback会调用我们的Runnable对象。

而对于mCallback对象,在构造方法中传入的,因为平常没有用过,都是使用无参的构造函数,基本上都为null。

那么,通过以上的分析,整个流程就比较清晰了:

  • ActivityThread.main()中调用Looper.prepareMainLooper()Looper.loop()方法。
  • Looper.prepareMainLooper()方法创建Looper对象,同时Looper对象中会保存与其对应的MessageQueue对象。
  • Looper.loop()调用之后对MesageQueue对象进行轮询。
  • 在创建Handler的时候,会保存当前线程的Looper对象和消息队列MessageQueue
  • 通过调用handler.sendXXX()将消息推送到消息队列。
  • 消息队列中实际上调用的是handler.handlerMessage()方法,产生了联系。

子线程和主线程的使用区别

在实际使用中,子线程如果想要构造handler对象,必须调用Looper.prepare()Looper.loop()方法。而主线程不需要,为什么呢?

如果对于上面的源码分析理解清楚了可以清楚的知道,因为主线程中已经在ActivityThread.main()即应用打开的初期初始化过Looper对象并开始了消息队列。

0 0