handler机制详解

来源:互联网 发布:走路领红包软件 编辑:程序博客网 时间:2024/05/16 14:44

Handler详解

  • 在ui线程中我们之后new Handelr()然后实现子线程和主线程的通讯是没有问题的。那是因为在ActivityThread类中调用了Loop.Preapre();和Looper.loop()方法 现在我们在子线程模拟这个过程
  • 在子线程中
  • 代码如下
    new Thread(){
    public void run() {

  •  Handler handler=new Handler();    };}.start();
    • 发现异常 java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
    • 我们只是写了new Handler()就报了一个异常,因此我们查看下构造方法
      mLooper = Looper.myLooper();
      if (mLooper == null) {
      throw new RuntimeException(
      "Can't create handler inside thread that has not called Looper.prepare()");
      }
    • 构造方法中先调用Looper.myLooper() ,之后判断是否为null,如果为null 抛出异常 因此我们可以肯定,这里的mLooper肯定为null
    • 因此我们得查看Looper.myLooper()方法是怎么工作的
    • Looper.myLooper(); 源码public static Looper myLooper() {
      return sThreadLocal.get();
      }
    • 这里引入了一个sThreadLocal.get();本地线程的get方法,我们继续查看get()返回的是什么?
    • 首先我们产看ThreadLocal类的功能

    Implements a thread-local storage, that is, a variable for which each thread
    has its own value. All threads share the same {@code ThreadLocal} object

    发现ThreadLocal就是给线程储存了一个共享的对象
    • 接下来我们看一看Looper.Prepare() 方法完成了什么

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

    • 可以看到sThreadLocal将我们的Looper对象存放到了线程中
    • 这也就能解释当我们new Hander()的时候,抛出异常,是因为直接Looper.myLooper();拿到的为空,如果我们prepare()的话 ,就给sThreadLocal存放了一个looper,因此也就不为null了,通过源代码,也可以发现一个线程只能有一个looper和Message,但是可以有多个handelr
    • 那么looper.loop()方法的作用是什么呢?是让消息泵循环起来,去Messagequeue中读取消息,不loop起来的话 sendMessage()无效
    • ` public static void loop() {
      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();for (;;) {    Message msg = queue.next(); // might block    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);    }    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();}

      }`

    • 大家看源代码可以看到 Looper.loop()首先拿到自己的Looper,之后拿到消息队列,也就是MessageQueue,因此可以看到MessageQueue在Looper中,因此一个线程只能有一个Looper,也只能有一个MessageQueue
    • 之后是一个死循环,不断的调用msg.next();可能被阻塞,类似Socket的服务器端,之后分发出去
    • 接着我们看sendMessage方法

     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {    msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);   }
    • 最终将消息存放到了消息队列中 ,looper的话就直接从MessageQueue死循环读取

    总结

    • handler.sendMessage()的功能是将Message()放到Hander中的MessageQueue中
    • Looper.prepare()是将Looper绑定到本线程ThreadLocal中
    • new Handelr()的时候 获得了ThreadLocal中绑定的Looper
    • Looper.loop()的时候,是死循环遍历MessageQueue,拿到消息给了msg.target,这个target指的就是发送消息的那个handelr
    • 一个thread中只能有一个Looper和MessageQueue,但是可以多个handler,因为在handler.sendMessage()的时候,Message被放到messagequeue的时候,已经和发送的hanlder绑定了,之后再loop的时候是msg.target.dispatch分发出去
    • handler和Looper共用的是MessageQueue
    • Messagequeue是Looper的一个成员变量
    • ThreadLocal 是保存线程共享的内容的,这里用来保存了Looper


0 0
原创粉丝点击