A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 阳丹老师 于 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。


1 个回复

倒序浏览
谢天成 2016-2-24 16:54:47
沙发
提示: 作者被禁止或删除 内容自动屏蔽
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马