handler机制

前言: 很早以前,学习android的时候就接触过Handler ,知道Handler是一个用于线程间通信的类,最常用于做下载条,最近,看了Pro android 3 这本书,里面描述的Handler 说得非常的细致,与此,写下Handler的学习笔记

Android 运行的进程

 为了,更好的了解Handler的机制,我们应该首先,将Android系统整个运行进程都要烂熟于心,下面是android 进程运行图:

handler机制_第1张图片 

从图中我们可以看到,当我们从外部调用组件的时候,Service 和 ContentProvider 是从线程池那里获取线程,而Activity 和BroadcastReceiver是直接在主线程运行,为了,追踪线程,我们可以用debug 方法,或者使用一个工具类,这里,我们创建一个用于监视线程的工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
  * @author Tom_achai
  * @ date  2011-11-20
  *
  */
public class Utils {
     public static long getThreadId(){
         Thread t = Thread.currentThread();
         return  t.getId();
     }
     
     /**
      * 获取单独线程信息
      * @ return
      */
     public static String getThreadSignature(){
         Thread t = Thread.currentThread();
         long l = t.getId();
         String name = t.getName();
         long p = t.getPriority();
         String gname = t.getThreadGroup().getName();
         return  ( "(Thread):" +name+ ":(id)" + l + "(:priority)"  + p + ":(group)"  + gname );
     }
     
     
     
     /**
      *获取当前线程 信息
      */
     public static void logThreadSignature(){
         Log.d( "ThreadUtils" , getThreadSignature());
     }
     
     public static void logThreadSignature(String name ){
         Log.d( "ThreadUtils" , name + ":" +getThreadSignature());
     }
     
     public static void sleepForInSecs(int secs){
         try{
             Thread. sleep (secs * 1000);
         }catch (Exception e) {
             //  TODO: handle exception
             e.printStackTrace();
         }
     }
     
     /**
      * 讲String放进Bundle 中
      * @param message
      * @ return
      */
     public static Bundle getStringAsBundle(String message){
         Bundle b = new Bundle();
         b.putString( "message" , message);
         return  b;
     }
     
     /**
      *
      * 获取Bundle的String
      * @param b
      * @ return
      */
     public static String getStringFromABundle(Bundle b){
         return  b.getString( "message" );
     }
}

有了这样一个类就可以方便我们观察线程的运行

好了,现在准备好以后就进入正题Handler

Handlers

为什么要使用Handlers?

    因为,我们当我们的主线程队列,如果处理一个消息超过5秒,android 就会抛出一个 ANP(无响应)的消息,所以,我们需要把一些要处理比较长的消息,放在一个单独线程里面处理,把处理以后的结果,返回给主线程运行,就需要用的Handler来进行线程建的通信,关系如下图;

     handler机制_第2张图片

下面是Handler,Message,Message Queue 之间的关系图

handler机制_第3张图片

这个图有4个地方关系到handlers

1, 主线程(Main thread)

2, 主线程队列(Main thread queue)

3,Hanlder

4,Message

   上面的四个地方,主线程,和主线程的队列我们无需处理,所以,我们主要是处理Handler 和 Message 之间的关系.

   我们每发出一个Message,Message就会落在主线程的队列当中,然后,Handler就可以调用Message绑定的数据,对主线程的组件进行操作.

Message

作为handler接受的对象,我们有必要知道Message这个数据类型是个怎样的数据类型

从官方文档中我们可以知道message 关于数据的字段

   
public int what  
public int arg1  
public int arg2  
public Object obj  

从上面的表格可以看出,message 提供了一个对象来存储对象,而且,还提供了三个int字段用来存储少量int类型

当然,除了以上三个Message 自有的字段外,我们还可以通过setData(Bundle b),来存储一个Bundle对象,来存储更丰富的数据类型,例如,图片等等.

在初始化我们的message的时候就可以为我们的Message默认字段赋值,注意赋值顺序!!!

1
2
3
4
5
6
7
8
9
Message msg = obtainMessage();
// 设置我们what 字段的初值,注意顺序!!!
Message msg = mHandler.obtainMessage(int what);
 
// 下面同理
Message msg = mHandler.obtainMessage(int what,Object object);
Message msg = mHandler.obtainMessage(int what,int arg1,int arg2);
Message msg = mHandler.obtainMessage(int what,int arg1,int arg2, Object obj
);



二;

handler机制的原理

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。 

1.Handler创建消息

        每一个消息都需要被指定的Handler处理,通过Handler创建消息便可以完成此功能。Android消息机制中引入了消息池。Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。使用消息池的好处是:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。消息池提高了消息对象的复用,减少系统垃圾回收的次数。消息的创建流程如图所示。

handler机制_第4张图片

2.Handler发送消息

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

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

handler机制_第5张图片

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

3.Handler处理消息

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

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

handler机制_第6张图片



你可能感兴趣的:(handler机制)