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

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

程序员文章站 2022-07-14 19:41:18
...

1.前言

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

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

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

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

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

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

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

转载于:https://my.oschina.net/u/2421076/blog/701591