Handler机制

来源:互联网 发布:商桥2016软件下载 编辑:程序博客网 时间:2024/06/06 07:28
Andriod提供了Handler 和 Looper 来满足线程间的通信。Handler先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(MessageExchange)。

  1. Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列)。
  2. Handler: 你可以构造Handler对象来与Looper沟通,以便push新消息到MessageQueue里;或者接收Looper从Message Queue取出)所送来的消息。
  3. Message Queue(消息队列):用来存放线程放入的消息。 
  4. 线程:UIthread 通常就是main thread,而Android启动程序时会替它建立一个MessageQueue。 

小结:handler机制即,我们构造一个Handler对象与线程产生的Looper对象进行沟通,根据Looper生成并管理下MessageQueue中的Message分析所得参数,将message送到对应的handler进行处理。

Handler对象、Looper对象、Message、MessageQueue为机制中的四主体 

1、handler机制消息流程

1.1 Handler创建消息

每一个消息都需要被指定的Handler处理,通过Handler创建消息便可以完成此功能。Android消息机制中引入了消息池。Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。

使用消息池的好处是:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。消息池提高了消息对象的复用,减少系统垃圾回收的次数。消息的创建流程如图所示。

1.2 Handler发送消息

UI主线程初始化第一个Handler时会通过ThreadLocal创建一个Looper,该Looper与UI主线程一一对应。使用ThreadLocal的目的是保证每一个线程只创建唯一一个Looper。之后其他Handler初始化的时候直接获取第一个Handler创建的Looper。Looper初始化的时候会创建一个消息队列MessageQueue。至此,主线程、消息循环、消息队列之间的关系是1:1:1。

Handler、Looper、MessageQueue的初始化流程如图所示:

Hander持有对UI主线程消息队列MessageQueue和消息循环Looper的引用,子线程可以通过Handler将消息发送到UI线程的消息队列MessageQueue中。

1.3 Handler处理消息

UI主线程通过Looper循环查询消息队列UI_MQ,当发现有消息存在时会将消息从消息队列中取出。首先分析消息,通过消息的参数判断该消息对应的Handler,然后将消息分发到指定的Handler进行处理。

子线程通过Handler、Looper与UI主线程通信的流程如图所示。

 

如果是使用Thread的run()方法,run()结束之后没有返回值。所以必须要自己建立通信机制。但是,其实使用Handler+Thread机制其实完全可以替代AsynTask的这种调用机制。只要将Handler对象传给Thread,就可以进行方便的异步处理。且这种MVC模式结构更加明显,方便管理。所以我觉得,使用asynTask还是Handler+Thread结构,个人喜好吧。但是有一点可以明显能感觉到得是,Handler+Thread适合进行大框架的异步处理,而asynTask适用于小型简单的异步处理。以上都是个人观点+理解。有新观点请指出。

2、Looper源码分析

Looper主要有prepare()和loop()两种方法

2.1 prepare()

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

sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。可以看到,在第5行,将一个Looper的实例放入了ThreadLocal,并且2-4行判断了sThreadLocal是否为null,否则抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例

2.2 looper()构造方法

1 private Looper(boolean quitAllowed) {  2     mQueue = new MessageQueue(quitAllowed);  3     mRun = true;  4     mThread = Thread.currentThread();  5 } 

在构造方法中,创建了一个MessageQueue(消息队列)。

2.3 loop()

 1 public static void loop() { 2     final Looper me = myLooper(); 3     if (me == null) { 4         throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 5     } 6     final MessageQueue queue = me.mQueue; 7  8     // Make sure the identity of this thread is that of the local process, 9     // and keep track of what that identity token actually is.10     Binder.clearCallingIdentity();11     final long ident = Binder.clearCallingIdentity();12 13     for (;;) {14         Message msg = queue.next(); // might block15         if (msg == null) {16             // No message indicates that the message queue is quitting.17             return;18         }19 20         // This must be in a local variable, in case a UI event sets the logger21         Printer logging = me.mLogging;22         if (logging != null) {23             logging.println(">>>>> Dispatching to " + msg.target + " "24                     + msg.callback + ": " + msg.what);25         }26 27         msg.target.dispatchMessage(msg);28 29         if (logging != null) {30             logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);31         }32 33         // Make sure that during the course of dispatching the34         // identity of the thread wasn't corrupted.35         final long newIdent = Binder.clearCallingIdentity();36         if (ident != newIdent) {37             Log.wtf(TAG, "Thread identity changed from 0x"38                             + Long.toHexString(ident) + " to 0x"39                             + Long.toHexString(newIdent) + " while dispatching to "40                             + msg.target.getClass().getName() + " "41                             + msg.callback + " what=" + msg.what);42         }43 44         msg.recycle();45     }46 }

第2行:
  public static Looper myLooper() {
    return sThreadLocal.get();
  }
方法直接返回了sThreadLocal存储的Looper实例,如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。
第6行:拿到该looper实例中的mQueue(消息队列)
13到45行:就进入了我们所说的无限循环。
14行:取出一条消息,如果没有消息则阻塞。
27行:使用调用 msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理。Msg的target是什么呢?其实就是handler对象。
44行:释放消息占据的资源。

2.4 Looper主要作用:

  1. 与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
  2. loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。

http://blog.csdn.net/itachi85/article/details/8035333

http://www.it165.net/pro/html/201404/12969.html

http://www.2cto.com/kf/201203/122729.html

http://blog.csdn.net/lmj623565791/article/details/38377229

0 0
原创粉丝点击