安卓消息处理机制(Looper,Handler,Message)

1.前言

android消息处理机制很基础,也很重要。一般耗时操作需要放到子线程中去执行,执行完后,需要把结果反馈给主线程,在主线程中更新ui。比如:在子线程中加载一张网络图片,加载成功后,在主线程中显示图片。那么,子线程怎样把结果反馈给主线程?这就用到了我们今天讲到的消息处理。

2.简要说明消息处理过程

消息处理机制主要涉及四个类:Looper,Message,MessageQueue,Handler。过程如下:

一个线程有且只有一个Looper对象,Looper是循环的意思,Looper对象创建自己的消息队列MessageQueue,当有消息message产生时,message会被压入队列message queue。Looper一直循环做如下工作:从message queue中取出一个 msg,handler来处理。借用一张图:

image

具体怎么处理下面会详细说明。

3. Looper

先来说说Looper。

Looper主要作用:

  1. 与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue(创建消息队列)。

  2. loop()方法不断从MessageQueue中去取消息,交给消息Message的target属性的dispatchMessage()去处理。

Looper中的变量:

// 每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;//looper中的消息队列
final Thread mThread;//绑定的线程

Looper通过prepare()方法来创建looper对象:

//quitAllowed:消息循环是否可以退出(主线程传进去的是false)
private static void prepare(boolean quitAllowed) {
        //如果一个线程中已经有looper对象,再创建就会抛出异常,也就是说,一个线程只能有一个looper对象
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

调用这个方法之后,需调用loop()方法来从消息队列中取出消息:

public static void loop() {
        //得到当前线程的Looper实例
        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;
        // 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();
        //looper开始无限循环
        for (;;) {
            Message msg = queue.next(); // 取出对列头的message(也可以叫task)
            if (msg == null) {
                //如果消息队列中没有task,就退出循环
                return;
            }
            //日志
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
            //使用调用 msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理。
            // Msg的target是什么呢?其实就是handler对象,下面会进行分析。
            msg.target.dispatchMessage(msg);
            //日志
            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }
            /*
            clearCallingIdentity这个可以看成是安全性代码,也可以看成是调试代码
            作用是确定当前这个looper所在的“线程”是否一直在同一个“进程”里,如果进程变多半是说明这个线程运行在某种跨进程代码里。
            比如说你通过AIDL调用stub,远程那边接到之后启动一个线程,就有可能触发ident != newIdent了
            */
            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();
        }
    }
    
    public static MessageQueue myQueue() {
        return myLooper().mQueue;
    }
    
    /*
    * 得到当前线程的Looper实例
    * */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }

如果要结束loop()循环,可以调用quit()退出循环。

public void quit() {
        mQueue.quit(false);
    }

注意:

  1. looper方法必须在prepare方法之后运行

  2. 每个线程有且最多只能有一个Looper对象,它是一个ThreadLocal;一个消息队列(如果一个线程已经有looper对象,再创建会抛异常)

  3. Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行

  4. Looper使一个线程变成Looper线程。

另外,Looper中还有一些方法看一下:

    //获取message queue    
    public static MessageQueue myQueue() {
        return myLooper().mQueue;
    }

    /*
    * 得到当前线程的Looper实例
    * */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }

/*
    * 给主线程初始化一个MainLooper,MainLooper一般是系统create,所以自己永远不用调用,知道有这么个东西就好了。
    * */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    /*
    * 得到主线程的Looper实例
    * */
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

4.Message

上面讲到,从looper中取出一个msg后,通过这句代码交给msg的target来处理消息msg.target.dispatchMessage(msg)

target是什么?

我们来看看Message类有什么东西。先看看有哪些变量:

Handler target;//target是处理消息的Handler对象
Runnable callback;//回调callback

private static Message sPool;
private static int sPoolSize = 0; /

//message内容
public int what;
Bundle data;
public Object obj;
public int arg1;
public int arg2;

获取Message实例可以直接new 一个Message,也可以通过调用Message的obtain()方法,Message中obtain()方法有很多,这里只看一个。

public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

所以,Message类中有一个Handler类型的变量target,来处理消息。真正处理消息的,是Handler类。下面我们来看Handler类。

注意:

产生一个Message对象,可以new,也可以使用Message.obtain()方法;

两者都可以,但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存。

5.Handler

依旧先看看变量:

final MessageQueue mQueue;//handler所在的线程的消息队列
final Looper mLooper;//handler所在线程的looper对象
//这个callback是一个接口,create handler对象的时候需要实现它。里面有一个handlerMessage()方法,
//handlerMessage()方法就是我们需要自己实现的处理消息的方法。
final Callback mCallback;

CallBack接口:

public interface Callback {
        public boolean handleMessage(Message msg);
    }

我们通常这样用:

Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    break;
            }
        }
    };

熟悉吧?这个handlerMessage()就是Handler类中的interface mCallback 里的handlerMessage()方法。

前面说过,looper从消息队列中取出一个message,通过message的target.dispatchMessage(msg)(Handler)来处理该消息。那么,Handler是如何与MessageQueue联系上的,它在子线程中发送的消息(一般发送消息都在非UI线程)又是怎么发送到MessageQueue中的?

我们来看看Handler的构造方法:

public Handler(Callback callback, boolean async) {
        //不用管
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> 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());
            }
        }
        //得到当前线程中的looper对象
        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;
    }

ok,上面疑问搞清楚了。我们接着来看之前一直说的dispatchMessage(msg)方法。

/*
* 这个方法处理消息
* 如果msg的callback和target(handler)都有值,会执行哪个?
* 通过getPostMessage(Runnerable r)方法可以知道,r即指定msg的callback。
* */
public void dispatchMessage(Message msg) {
    //先执行msg的callback
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        //如果msg的callback为空,则执行msg的handler对象target的回调mCallback。
        // 这个mCallback就是我们平时创建handler时实现的接口handleressage()。
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

好了,到这里,消息处理就说完了。handler类里有一些发送消息的方法,post(Runnable r)sendMessageDelayed(Message msg, long delayMillis)sendMessageAtTime(Message msg, long uptimeMillis)postAtTime(Runnable r, long uptimeMillis)等等,它们的作用都是把一个msg压入message queue中等待处理。如果想知道 post和sendMessage的区别,怎么将msg压入message queue,这些方法的具体实现等,可以看我的博客 postDelayed, sendMessageAtTime等handler发送消息方法总结

参考资料:

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

  2. android的消息处理机制(图+源码分析)——Looper,Handler,Message

推荐阅读更多精彩内容