Android的Handler,Message,Looper的原理详解

狂风中的少年 提交于 2020-03-17 16:46:20

某厂面试归来,发现自己落伍了!>>>

先说说Looper类:Looper就是为每一个线程创建一个Looper对象,维护一个MessageQueue,并循环从MessageQueue中取出消息并分发给Handler执行。下面是Looper源码中如何使用的一个示例

class LooperThread extends Thread {

  *      public Handler mHandler;

  *

  *      public void run() {

  *          Looper.prepare();

  *

  *          mHandler = new Handler() {

  *              public void handleMessage(Message msg) {

  *                  // process incoming messages here

  *              }

  *          };

  *

  *          Looper.loop();

  *      }

  *  }

我们看看Looper.prepare干了些什么事

public static void prepare() {

        prepare(true);

    }


    //判断当前线程是否有Looper对象,没有则new一个并放到ThreadLocal中

    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来存储Looper对象呢:

我们知道ThreadLocal是与线程相关的一个类,系统规定每个线程只能有一个Looper对象,也就是说只能由一个MessageQueue对象,这样便于维护消息队列。如果我已经prepare一次了,那么我的当前线程中是有Looper对象的,并存储在当前线程变量内。当你再在此线程prepare时,ThreadLocal对象会获取当前线程是否有Looper对象,如果有,直接抛异常,这是系统所不允许的,

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

因此ThreadLocal用来存储Looper对象,设计的真鸡贼,不知道大家还有没有别的方式来控制一个线程只有一个Looper对象。看到很多博客都说ThreadLocal是用来并发控制资源共享的,ThreadLocal存储每个线程对资源的副本,再次本人觉得纯属扯淡,你去看看ThreadLocal的例子,如果ThreadLocal存储副本,你丫的把“主本”给我找出来?通过Looper就可以看出来ThreadLocal只是存储当前线程的相关变量。

创建完Looper对象,并初始化了MessageQueue对象,下面该进入不断循环,取出消息了:

public static void loop() {

        final Looper me = myLooper();//你看,每次都是myLooper获取当前线程的Looper对象,免得获取到别的线程的了

        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();


        for (;;) {//死循环吧

            Message msg = queue.next(); // 从队列中获取下一个

            if (msg == null) {

                // No message indicates that the message queue is quitting.

                return;

            }


            // This must be in a local variable, in case a UI event sets the logger

            Printer logging = me.mLogging;

            if (logging != null) {

                logging.println(">>>>> Dispatching to " + msg.target + " " +

                        msg.callback + ": " + msg.what);

            }


            //获取到消息之后分发给HAndler处理,其实这个Handler就是target

            msg.target.dispatchMessage(msg);


            if (logging != null) {

                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);

            }


            // 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.recycle();//回收Message对象,重复利用就在此,哈哈。。下面会讲

        }

    }



至此,Looper的主要工作讲完了。

接着是Message,就是携带了一些数据,被handler带着飞。重复利用问题以及链表的结构。

先看看都带了什么数据:

/*package*/ int flags;


    /*package*/ long when;//时间而已

    

    /*package*/ Bundle data;//数据

    

    /*package*/ Handler target;     //Handler,表明我这个Message要被哪个Handler处理,当然是谁发送我,谁处理,发送我的时候给我赋值

    

    /*package*/ Runnable callback;  //post(Runnable r)函数使用 

    

    // sometimes we store linked lists of these things

    /*package*/ Message next;//链接下一个Message

再看看重复利用:


public void recycle() {

        clearForRecycle();//把要回收的这个Message数据清空


        synchronized (sPoolSync) {

            if (sPoolSize < MAX_POOL_SIZE) {//不超过50个,源码的常量

                next = sPool;

                sPool = this;//关键在这,又把这个对象赋给了静态的sPool引用,gc不会回收此Message

                sPoolSize++;

            }

        }

    }

 public static Message obtain() {

        synchronized (sPoolSync) {

            if (sPool != null) {

                Message m = sPool;

                sPool = m.next;

                m.next = null;

                sPoolSize--;

                return m;

            }

        }

        return new Message();

    }



看吧,我们obtain的时候会利用刚才回收掉的Message,否则才new Message();

再看MessageQueue,他里面并不真正的使用数组的形式去存储一系列的Message,而是通过Message的next使用链表的形式维护Message队列,只要能拿到链表的第一个Message就可以展开一系列的循环读取Message。通过next()方法获取。代码就不贴了。

最后是Handler类介绍:创建一个Handler对象,一般都会重写handleMesage方法。去处理我们发送过来的消息,handleMessage何时被调用呢,看下

 public void dispatchMessage(Message msg) {

        if (msg.callback != null) {

            handleCallback(msg);//处理handler.post(Runnable r)的

        } else {

            if (mCallback != null) {

                if (mCallback.handleMessage(msg)) {

                    return;

                }

            }

            handleMessage(msg);//调用了吧

        }

    }

那么dispatchMessage的调用呢,看前面的Looper.loop(),娶到一个Message之后,通过Message 的target(Handler)调用到的,这样处理的过程。

再看看Handler的发送:

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);

    }

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

        msg.target = this;//这个很重要就是把此Handler对象直接赋给此Message的target,

        if (mAsynchronous) {

            msg.setAsynchronous(true);

        }

        return queue.enqueueMessage(msg, uptimeMillis);//这个就是把此Message对象加入MessageQueue

    }



这样,整个流程就走完了,发送到加入队列,再取,在处理的流程。

发送是Handler,加入队列是MessageQueue,取是Looper(内部也是MesageQueue取的,不过是有Looper维护此队列而已),处理是Handler。

Activity主线程,在程序中的main方法已经prepare了,并且loop了,所以我们没必要创建Looper对象。

在我们自己创建的子线程中,我们要想处理消息,必须Looper.prepare,并且loop()。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!