本帖最后由 阳丹老师 于 2016-2-24 11:40 编辑
Handler机制深入理解
众所周知,Android4.0以后不能在UI线程访问网络,而子线程也不能更新UI界面。为了根据下载进度实时更新UI界面,就需要用到Handler消息机制来实现线程间的通信。
Handler机制主要包括四个关键对象,分别是:Message、Handler、MessageQueue、Looper。下面对这四个关键对象进行简要的介绍。 1、Message
Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。Message的what字段可以用来携带一些整型数据,obj字段可以用来携带一个Object对象。
2、Handler Handler顾名思义就是处理者的意思,它主要用于发送消息和处理消息。一般使用Handelr对象的sendMessage()方法发送消息,发出的消息经过一系列的辗转处理后,最终会传递到Handler对象的handlerMessage()方法中。
3、MessageQueue MessageQueue是消息队列的意思,它主要用来存放通过Handler发送的消息。通过Handler发送的消息会存在MessageQueue中等待处理。每个线程中只会有一个MessageQueue对象。
4、Looper Looper是每个线程中的MessageQueue的管家。调用Looper的loop()方法后,就会进入到一个无线循环中。然后每等发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的HandlerMessage()方法中。此外每个线程也只会有一个Looper对象。在主线程中创建Handler对象时,系统已经为我们创建了Looper对象,所以不用手动创建Looper对象,而在子线程中Handler对象,我们需要调用Looper.loop()方法开启消息循环。
为了让初学者更好的理解Handler消息机制,接下来我们通过一个图例来梳理一下整个Handler消息处理流程,如下图所示。 file:///C:/Users/PC-YAN~1/AppData/Local/Temp/msohtmlclip1/01/clip_image002ec559dcf-45fa-4e35-adfe-f62e12595330.gif 从上图中可以清晰地看到整个Handler消息机制处理流程。Handler消息处理首先需要在UI线程创建一个Handler对象,然后在子线程中调用Hanlder的sendMessage()方法,接着这个消息会存放在UI线程的MessageQueue中,通过Looper对象取出MessageQueue中的消息,最后分发回Hanlder的handleMessage()方法中。
问题拓展
1、 为什么主线程可以创建Handler对象,而自己创建的线程直接创建Handler就会报 file:///C:/Users/PC-YAN~1/AppData/Local/Temp/msohtmlclip1/01/clip_image0028a604494-586d-462f-8863-6e2b8ba879b1.jpg 我们先去看一下主线程启动的时候做了什么。
file:///C:/Users/PC-YAN~1/AppData/Local/Temp/msohtmlclip1/01/clip_image002620d1c46-ec84-49af-a6ec-a53735faff76.jpg
这是从 ActivityThread.java类中截取的main函数的代码, 这里有两行关键代码: Looper.prepareMainLooper(); Looper.loop(); 查一下api文档,找到Looper,如下: file:///C:/Users/PC-YAN~1/AppData/Local/Temp/msohtmlclip1/01/clip_image002e450db60-b27e-4dfb-b168-0d3319a53609.jpg
[size=1.4]在类的概述中,官方的描述是:Looper的作用是为线程创建消息轮询器。线程默认没有消息轮询器与他关联,如果想关联就要调用Looper.perpare(),然后调用 Looper.loop();然后官方给出了示例代码。 但是我们在源代码中看到的是 Looper.perpareMainLooper();查找文档: file:///C:/Users/PC-YAN~1/AppData/Local/Temp/msohtmlclip1/01/clip_image004be013053-f910-4104-b6c1-449b495429cf.jpg 文档中的说明是初始化该线程应用的主线程,还说这个方法由Android 环境为我们调用,我们自己不用调用。 file:///C:/Users/PC-YAN~1/AppData/Local/Temp/msohtmlclip1/01/clip_image00647862641-bd8d-4a62-893b-d5f4e2c8ba1a.jpg 我们看一下该方法的源代码,还是调用了 prepare()方法,只是多了 setMainLooper();和设置该Looper不能退出。 由此我们可以总结出:自己的线程如果也想创建Handler[size=1.4]必须调用Looper[size=1.4]的prepare[size=1.4]与loop[size=1.4]两个方法。
2、 Looper的prepare和loop中到底做了什么? file:///C:/Users/PC-YAN~1/AppData/Local/Temp/msohtmlclip1/01/clip_image00851884442-de14-46ef-8e41-66322ed0f700.jpg
file:///C:/Users/PC-YAN~1/AppData/Local/Temp/msohtmlclip1/01/clip_image010d08c4745-d28a-4e53-9caa-bd7141ab008c.jpg prepare方法只是创建了一个Looper然后关联到当前线程, loop方法就是去消息队列不停地读取消息,而且如果消息队列中没有消息的时候该线程就会被阻塞,注意 might block。
|