Handler机制

来源:互联网 发布:创维32e300e数据 编辑:程序博客网 时间:2024/06/06 01:40

Android应用在启动的时候会开启一条主线程,也叫UI线程,在UI线程里面不能执行一些耗时操作,不然的话会使UI失去响应,会出现ANR。所以我们执行一些耗时操作(比如下载)的话需要另开子线程来执行,执行完成后可能需要更新UI(比如将下载下来的信息显示出来),但是Android只允许在主线程里面更新UI,因为UI线程是线程不安全的,这应该就是Handler机制产生的原因。
Looper、Handler、MessageQueue、

public static final void prepare() {          if (sThreadLocal.get() != null) {              throw new RuntimeException("Only one Looper may be created per thread");          }          sThreadLocal.set(new Looper(true));  }  

Looper.prepare()方法会判断ThreadLocal是否已经保存了Looper,是则抛出异常,这说明,一个线程只能有一个Looper(),为什么说一个线程只有一个Looper(),这个只有弄清楚了ThreadLocal的工作原理才能明白。
简单说一下就是:

sThreadLocal.set(new Looper(true))//其实等同于以下Thread.currentThread.ThreadLocalMap<ThreadLocal, Object>.put(sThreadLocal, Looper);

总结一下prepare()做的事情就是:判断当前线程是否已经有Looper了,有的话,抛出异常,没有的话,实例化一个Looper添加到ThreadLocal中,添加到ThreadLocal中,该Looper就和当前线程关联了。
对于ThreadLocal的理解可以参考http://blog.csdn.net/actor1999/article/details/52121303

获取Looper的方法,可以看到这个方法静态的。

 public static @Nullable Looper myLooper() {        return sThreadLocal.get();    }

Looper的构造方法,可以看到实例化了一个MessageQueue

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

loop()方法,静态的

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

可以看到,loop方法会执行一个死循环,从Message中取出数据 Message msg = queue.next(); 该方法可能会阻塞。当有Message的时候,调用msg.target.dispatchMessage(msg),这里的target是一个Handler对象,查看源码可以发现,该方法最终会调用handlerMessage()方法,而这个方法是个空方法,这也是我们处理消息的地方

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

我们在调用Handler.sendMessage(Message msg)其他方法也一样,最终都是调用以下这个方法:

public boolean sendMessageAtTime(Message msg, long uptimeMillis)  {      boolean sent = false;      MessageQueue queue = mQueue;      if (queue != null) {          msg.target = this;          sent = queue.enqueueMessage(msg, uptimeMillis);      }      else {          RuntimeException e = new RuntimeException(              this + " sendMessageAtTime() called with no mQueue");          Log.w("Looper", e.getMessage(), e);      }      return sent;  }  

可以看到这个方法就是设置msg.target = this.
然后调用queue.enqueueMessage(msg, uptimeMillsi),这个方法其实就是将消息加入队列中。
Handler在实例化的时候,会保存当前线程的Looper,和该Looper的MeesageQueue,因此在实例化Handler的时候要先调用Looper.prepare();

为什么Looper要这么设计,一个线程只能有一个Looper,并且其构造函数是private的,我们从他出现的背景就能够领悟一二,文章开头说过了,他出现的背景是,子线程想更新UI,但是子线程又不能更新UI,只能通过这种消息机制来通知主线程更新UI,或者具体到,它就是在当前线程检索是否有消息需要传递的一个东西,一个线程只需要有一个检索的就够了。

总结下来,整个流程就是:

  1. 调用Looper.prepare(),创建和线程绑定的Looper,还有和Looper绑定的MessageQueue。
  2. 实例化Handler,获取Looper和MeesageQueue。重写其handMessage()方法。
  3. 调用Hander.sendMessage()方法,这个方法会将Message加入MessageQueue队列
  4. 然后loop()方法会将消息取出来,然后调用msg.target.dispatchMessage(msg)(Meesage.Handler.dispachMessage())最终调用我们重写的handlerMessage()方法。(loop()里面是一个死循环)
0 0
原创粉丝点击