本文适合对handler有一定了解的人阅读,另外,本文不涉及对handler机制的源码解读,只纯粹讲解handler的使用,加深读者对handler的了解和更好的使用handler。 通过本文你能了解如下内容:
1.HandlerThread类的使用;
2.不依赖源码的情况下理解Handler线程间通信的机制。
我们在开发应用程序的时候有时为了提高效率或不阻塞UI线程会创建多个线程来完成多个任务。为了能够让多个线程方便的通信,我们会使用
handler实现线程间的通信。
而在线程中使用handler需要保证当前线程包含Looper(
UI线程默认包含Looper,至于为什么需要Looper后面就会讲到)。在线程中创建Looper的方法如下:在线程run()方法中先调用Looper.prepare()方法初始化Looper,然后在run()方法最后调用Looper.loop()方法(
最后面调用是因为loop()方法默认是死循环的)。那么这里就有2个问题了,一是前面说的为什么需要Looper;二是实现Looper有没有简单的方法。
先说第一个问题,
为什么需要Looper?其实很简单,因为
handler是通过消息队列(MessageQueue)实现的,既然是队列那就需要一个
循环不停从队列中取出消息并交给
handler来处理,这个循环就是Looper。而这个消息队列就是在我们创建Looper时一并创建的(也就是prepare()方法)并由Looper所在的线程负责管理,那么消息是怎么放到消息队列中的呢?这个就是由
handler实现的,调用handler的sendMessage方法(
主要有有四个方法可以调用)。当然,还有一个问题就是
handler怎么把消息发送到特定的MessageQueue的?这里有两种方式可以可以做到:
一是在创建
handler时在构造方法中指定looper,如:
这里的mWorkThread.getLooper就是获取mWorkThread线程的looper,这样消息就发送给mWorkThread线程所在的MessageQueue了;
二是在处理
handler的线程中创建,如:
这样的话先通过Looper.prepare()就在当前线程创建一个MessageQueue了那么handler就默认发送到当前线程的MessageQueue中了。
在说第二个问题,
实现Looper有没有简单的方法?答案是肯定的,并且就是我接下来要讲的
handler的两种使用方式中的第一种,这两种方式如下:
1.无需刷新UI型,这种类型就是从UI线程发送消息到其他线程中去处理不需要把处理结果返回给UI线程。那么这种类型就可以使用更为简单的方法来实现Looper:使用Android提供的HandlerThread类,官方文档对HandlerThread的描述如下:
使用步骤如下:(
以下操作都是在UI线程中)
a.创建一个HandlerThread的实例,即创建一个包含Looper的线程:
文档最后明确规定一定要调用start()方法启动该线程。
b.创建一个Handler并传入a中线程的Looper:
这样就创建好了一个
handler,接下来就是实现Handler.Callback接口并使用handler的发送消息的方法发送消息,如下:
完整代码如下:
拓展:这种类型不仅仅可以用于UI线程中,其他类似工作线程也可以,只要是无需返回的都可以。
2.刷新UI型,这种类型就是需要在子线程完成工作后把结果告知UI线程,例如:网络下载完成百分比等。那么这种情况就不适合使用HandlerThread了,这种情况就需要老老实实的创建子线程,在子线程中调用handler的sendMessage方法而在创建handler的传入UI线程的Looper,如下:
当然图中所示是一个很简单的例子,实际中比这要复杂些,比如,我们在UI线程中加一个按钮,点击这个按钮就会启动一个子线程去网上下载一本小说并且在下载完成后通知UI线程。那么只需要在图中的代码中加入一个按钮并且在run() 方法中加入下载的逻辑。在上图中我用了Handler的2种构造方法来创建Handler,
那么Handler是不是只有2种构造方法呢?,我们看下文档:
在这个图中,我们可以看到Handler总共有4种构造方法,大家可以想想另外2种是干吗用的?这里我就不讲了。(
答案就在图中)
上面介绍了Android线程间通信-Handler的使用,尤其后面介绍的两种使用方式(
我自己定义的两种使用方式,未得官方认可),第二种方式适用于双向交互的情景,这种情景尤其刷新UI居多;第一种方式更适合单向交互的情景,即我把任务源源不断地交给你去做但我不管你是否做完做好等等,然而事情没有绝对的,适用第一种方式确实需要通知UI任务的结果怎么办,其实也是可以的,就是在处理消息的方法中(handleMessage()方法)调用UI线程的handler的sendMessage方法将任务结果以消息的形式发给UI线程,当然前提是UI线程定义了对应的handler。
最后,这里总结下Handler的几种常见使用方式:
1.Handler绑定主线程并且在主线程创建Message消息并发送Handler消息;
2.Handler绑定子线程并且在子线程创建Message消息并发送Handler消息;
3.Handler绑定主线程并且在子线程创建Message消息并发送Handler消息,即刷新UI型;
4.Handler绑定子线程并且在主线程创建Message消息并发送Handler消息,即无需刷新UI型;