目录
一.概述
二.什么是Handler机制
三.Handler机制中的角色以及作用
四.Handler中涉及的方法浅析
五.Handler机制工作流程
六.小结
一、概述
在Android开发的过程中我们经常会接触到多线程开发的内容,既然讲到多线程,我们就不得不仔细分析一下主线程和子线程。在Android应用程序启动的时候,系统会创建一个主线程,负责UI组件进行交互,例如UI界面的显示更新等等内容。因为在主线程中会对OnClick、OnTouch等事件进行响应操作,所以我们通常也会称呼主线程为UI线程。
单线程模型的UI线程不安全。UI线程不安全的意思就是多线程访问资源的时候,有一定几率会造成多个数据先后更改数据造成前后数据不同一的情况。举个例子,就是说有A和B两个子线程同是要对一块资源进行操作,他们可以同一时间进行操作而不是说A先来就先去完成任务,然后再轮到B进行操作。像在这种不分先后同时进行的情况会造成当B想对一块资源进行操作的时候,这块资源在B操作之前已经被A进行了修改,这就造成了数据不一致的数据混乱情况。
那么什么是所谓的线程安全呢?线程安全我们就可以理解为当一个线程去访问一块资源的时候,我们会对这块资源有个保护的措施,例如加个锁(synchronized)。加锁后的情况就是当一个线程要对一块资源进行操作,在当前线程没有访问结束释放锁之前,别的想要对资源进行访问操作的线程只能等到释放锁后才能访问,这样的线程就是安全的线程。
所以当多个线程并发操作UI,就很有可能导致线程的安全问题,为了解决这个问题,Android制定了一个规则:只允许UI线程修改Activity里的UI,如果我们想动态的改变界面组件的属性值就需要用到Handler。
主线程(UI线程)只能处理一些简单且不耗时的工作,而像下载、访问网络或者数据库这种消耗时间比较长的工作,我们一般会在子线程进行操作。如果我们在主线程进行耗时操作,会引发线程性能大大下降甚至引发ANR问题,导致应用崩溃。单线程模型的UI线程不安全。UI线程不安全的意思就是多线程访问资源的时候,有一定几率会造成多个数据先后更改数据造成前后数据不同一的情况。举个例子,就是说有A和B两个子线程同是要对一块资源进行操作,他们可以同一时间进行操作而不是说A先来就先去完成任务,然后再轮到B进行操作。像在这种不分先后同时进行的情况会造成当B想对一块资源进行操作的时候,这块资源在B操作之前已经被A进行了修改,这就造成了数据不一致的数据混乱情况。
那么什么是所谓的线程安全呢?线程安全我们就可以理解为当一个线程去访问一块资源的时候,我们会对这块资源有个保护的措施,例如加个锁(synchronized)。加锁后的情况就是当一个线程要对一块资源进行操作,在当前线程没有访问结束释放锁之前,别的想要对资源进行访问操作的线程只能等到释放锁后才能访问,这样的线程就是安全的线程。
所以当多个线程并发操作UI,就很有可能导致线程的安全问题,为了解决这个问题,Android制定了一个规则:只允许UI线程修改Activity里的UI,如果我们想动态的改变界面组件的属性值就需要用到Handler。
二、什么是Handler机制
Handler是Android给我们提供的一套用来更新UI的机制,也是一套消息处理机制(或称为消息处理方法),我们可以发送消息,也可以通过它来处理消息。Handler和Looper满足了Android线程间的通信。
三、Handler机制中的角色以及作用
Looper:一个线程可以产生一个Looper对象,Looper用来管理线程中的MessageQueue(消息队列)。Looper是线程用来运行消息循环(message loop)的类。默认情况下,线程并没有与之关联的Looper,可以通过在线程中调用Looper.prepare()方法来获取,并通过Looper.loop()无限循环地获取并分发MessageQueue中的消息,知道所有消息全部处理。
Handler:可以构造Handler对象来与Looper沟通,以便将新的Message推送到Message,或者接受Looper从MessageQueue取出发送来的信息。
MessageQueue:消息队列,用来存放Message的集合,并由Looper实例来分发里面的Message对象。Message并不是直接加入到MessageQueue中的,而是通过与Looper对象相关联的MessageQueue.IdleHandler对象来完成的。我们可以通过Looper.myQueue()方法来获得当前线程的MessageQueue。消息队列虽然叫做队列,但不是真正意义上的队列,而是采用单链表的数据结构来存储消息列表。
Message:一个message对象包含一个自身的描述信息和一个可以发给handler的任意数据对象。这个对象包含了两个int类型的extra字段和一个object类型的extra字段。利用它们,在很多情况下我们都不需要自己做内存分配工作。
实例化Message的最好的方法是调用Message.obtain()或Handler.obtainMessage()。因为这两个方法是从一个可回收利用的message对象回收池中获取Message实例。该回收池用于将每次交给handler处理的message对象进行回收。
线程:UI Thread通常就是main thread,而Android启动程序时会替它建立一个MessageQueue。
四、Handler中涉及方法浅析
Handler的几个常见的构造方法:
1.Handler()默认构造方法,与当前线程及其Looper实例绑定。如果在主线程中执行new Handler(),那么该 handler实例所绑定的便是UI线程和UI线程绑定的Looper实例。
2.Handler(Handler.Callback callback)与当前线程及其Looper实例绑定,同时调用一个callback接口 (用于实现消息处理——即在callback中重写handlerMessage()方法)
3.Handler(Looper looper)将该新建的handler实例与制定的looper对象绑定。
4.Handler(Looper looper,Handler.Callback callback)指定该handler实例所绑定的looper实例并使用给 定的回调接口进行消息处理。
以上这些构造函数最终调用的其实都是Handler(Looper looper,Callback callback,boolean async),只 是参数缺少的自动补为null或false而已。
Handler允许我们将Message或Runnable对象发送到当前线程绑定的MessageQueue中,并通过Looper对象不断循环地从队列中获取Message或Runnable对象进行处理。因此Handler有两个主要的用途:
1.定时执行messages和runnables;
2.在将一个action入队并在其他线程中执行;
在第一个用途中,有以下几个方法可以用:
post(Runnable):将runnable对象入队。
postAtTime(Runnable , long):将runnable对象入队,并在指定时间执行
postDelayed(Runnable , long):将runnable对象入队,并经过指定时间后执行
sendEmptyMessage(int):发送只具有what标志值的message
sendMessage(Message):将一个message对象入队,且允许该message对象带有一些数据,如一个bundle类型的数据或一个int类型的标志值等,这些数据将在Handler的handleMessage(Message)方法中进行处理,当然,具体处理逻辑需要我们自己重写handleMessage()方法。
sendMessageDelayed(Message,long):将message入队,并在当前时间延迟指定时间长度前将该消息放在所有挂起的消息之后。
sendMessageAtTime(Message,long):将message入队,并在指定时间到之前将该消息放在所有挂起的消息之后。
Looper.prepare():获取线程中与之关联的Looper。
Looper.loop():无限循环的获取并分发MessageQueue中的消息,直到所有消息全部处理。
五、Handler工作流程
1.首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
2. Looper.loop()会让当前线程进入一个无限循环,不断从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
3.Handler的构造方法,会首先得到当前线程中保存的Looper实例,近而与Looper实例中的MessageQueue相关联。
4.Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。
5.在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。
Handler创建消息
每一个消息都需要被指定的Handler处理,通过Handler创建消息便可以完成此功能。Android消息机制中引入了消息池。Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。
Handler发送消息
UI主线程初始化第一个Handler时会通过ThreadLocal创建一个Looper,该Looper与UI主线程一一对应。使用ThreadLocal的目的是保证每一个线程只能创建唯一一个Looper。之后其他Handler初始化的时候直接获取第一个Handler创建的Looper。Looper初始化的时候会创建一个消息队列MessageQueue。
Handle处理消息
UI主线程通过Looper循环查询消息队列UI_MQ,当发现有消息存在时会将消息从消息队列中取出。首先分析消息,通过消息的参数判断该消息对应的Handler,然后将消息分发到指定的Handler进行处理。
六、小结
设计Handler机制的最根本目的是为了解决Android开发中的多线程并发和线程间通信的问题。有不足之处欢迎大牛们批评指出。