在android消息机制中Handler扮演着举足轻重的作用,(AsnyTask其实也是对Handler+Thread做了一层封装),ui线程超过5s就会报出ANR,一般耗时操作操作需要放在子线程中处理,这时候Handler就可以大展身手,Handler主要用来处理完耗时操作将访问UI的工作切换到主线程去。

1、原理篇

简单概括一下:

Handler创建时会采用当前线程的Looper来构建内部的消息循环系统如果当前线程没有Looper那么就会报错,因此使用时主要为当前线程创建Looper即可或者在一个有Looper的线程中创建Handler也可以,在我们的UI线程中系统已经在一开始就为我们创建好了,自定义的子线程如何创建后面会说。

首先需要搞定几个概念

【MessageQueue】是一种数据结构,见名知义,就是一个消息队列(注意不是数据结构的队列,其实是个单链表),存放消息的地方。每一个线程最多只可以拥有一个MessageQueue数据结构。创建一个线程的时候,并不会自动创建其MessageQueue。通常使用一个Looper对象对该线程的MessageQueue进行管理。主线程创建时,会创建一个默认的Looper对象,而Looper对象的创建,将自动创建一个Message
Queue。其他非主线程,不会自动创建Looper,要需要的时候,通过调用prepare函数来实现。

示例:

  1. public void run(){
  2. Looper.prepare(); //创建该线程的Looper对象
  3. //这里Looper.myLooper()获得的就是该线程的Looper对象了
  4. handler = new ThreadHandler(Looper.myLooper());
  5. Message msg = handler.obtainMessage(1,1,1,"我自己");
  6. handler.sendMessage(msg);
  7. Looper.loop();
  8.  
  9. }

俩行代码,该线程就具有自己的Looper啦

【Message】消息对象

MessageQueue里面可以存放Message的消息一个Message Queue中包含多个Message。Message实例对象的取得,通常使用Message类里的静态方法obtain(),该方法有多个重载版本可供选择;它的创建并不一定是直接创建一个新的实例,而是先从Message Pool(消息池)中看有没有可用的Message实例,存在则直接取出返回这个实例。

  1. /**
  2. * Return a new Message instance from the global pool. Allows us to
  3. * avoid allocating new objects in many cases.
  4. */
  5. public static Message obtain() {
  6. synchronized (sPoolSync) {
  7. if (sPool != null) {
  8. Message m = sPool;
  9. sPool = m.next;
  10. m.next = null;
  11. m.flags = 0; // clear in-use flag
  12. sPoolSize--;
  13. return m;
  14. }
  15. }
  16. return new Message();
  17. }

源码中可以看出如果Message Pool中没有可用的Message实例(poolsize=50),则才用给定的参数创建一个Message对象。调用removeMessages()时,将Message从Message Queue中删除,同时放入到Message Pool中。除了上面这种方式,也可以通过Handler对象的obtainMessage()获取一个Message实例。

通常Message Queue只有俩种单链表操作  插入(对应方法enqueueMessage)和读取(next)

【Looper】

是MessageQueue的管理者。每一个MessageQueue都不能脱离Looper而存在,Looper对象的创建是通过prepare函数来实现的。同时每一个Looper对象和一个线程关联。通过调用Looper.myLooper()可以获得当前线程的Looper对象创建一个Looper对象时,会同时创建一个MessageQueue对象。除了主线程有默认的Looper,其他线程默认是没有MessageQueue对象的,所以,不能接受Message。如需要接受,自己定义一个Looper对象(通过prepare函数),这样该线程就有了自己的Looper对象和MessageQueue数据结构了。Looper从MessageQueue中取出Message然后,交由Handler的handleMessage进行处理。处理完成后,调用Message.recycle()将其放入Message
Pool中。

Looper扮演着消息循环角色,一有消息就处理,否则就阻塞在那里。

小结一下:

当Handler创建完毕后,内部的Looper以及MessageQueue就可以和Handler一起协同工作了,然后通过Handler的post方法将一个Runnable放到Looper中或者通过send方法发送一个消息,同样也是放到Looper中处理(post最终还是通过send方法来完成的),send然后调用enqueueMessage来插入到消息链表中,Looper发现有新消息来到就会处理这个消息,最终Runnable或者Handler的Messager方法就会被调用,要注意looper是运行在handler所在的线程中,这样handler中的业务逻辑就被切换到创建Handler的逻辑中去了

关于更加详细的Handler分析可以参看http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html

2、使用篇

【handler】

对于Handler来说有两种主要的方式:

1. 计划好消息和Runnable将来的某一个时间点来执行它 2. 从一个不同的线程中执行Handler的入队操作。分发消息由下面的几个方法完成:

   1) post(Runnable),

   2) postAtTime(Runnable, long),

   3) postDelayed(Runnable, long),

4) sendEmptyMessage(int),

   5) sendMessage(Message),

   6) sendMessageAtTime(Message, long),

   7) sendMessageDelayed(Message, long)

   post方式的方法可以将一个Runable对象排列到消息队列中。sendMessage方式的方法可以通过 Handler的handleMessage(Message) 方法携带有bundle类型的数据的Message对象到队列中(需要你实现Handler的子类)。

【message】

定义一个message包含描述信息和任意的数据对象发送给Handler。这个对象包含两个额外的int类型的属性和一个Object类型的属性,它可以让你不需要去做一些强制类型的转换的操作。

1) arg1 和 arg2 都是Message自带的用来传递一些轻量级存储int类型的数据,比如进度条的数据等。通过这个数据是通过Bundle的方式来转载的,读者可以自己查阅源代码研究。

  2) obj 是Message自带的Object类型对象,用来传递一些对象。兼容性最高避免对齐进行类型转换等。

  3) replyTo 是作为线程通信的时候使用.

  4) what 用户自定义的消息码让接受者识别消息种类,int类型。

获得Message的构造方法最好的方式是调用Message.obtain() 和 Handler.obtainMessage()方法。以便能够更好被回收池所回收,而不是直接用 new Message的方式来获得Message对象

【避免使用不当造成内存泄露】

使用handler不当会造成内存泄露比如下面一段代码

  1. Handler mHandler = new Handler() {
  2. @Override
  3. public void handleMessage(Message msg) {
  4. mImageView.setImageBitmap(mBitmap);
  5. }
  6. }

当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然你怎么可能通过Handler来操作Activity中的View?)。而Handler通常会伴随着一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕(例如图片下载完毕)之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC
检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有 Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。另外,如果你执行了Handler 的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message
-> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。(参考自http://my.oschina.net/rengwuxian/blog/181449

通常我们使用弱引用来处理,比如如下的方式就非常优雅

  1. private MyHandler mHandler = null;
  2.  
  3. private static class MyHandler extends Handler {
  4. private WeakReference<WaterWaveProgress> mWeakRef = null;
  5.  
  6. private int refreshPeriod = 100;
  7.  
  8. public MyHandler(WaterWaveProgress host) {
  9. mWeakRef = new WeakReference<WaterWaveProgress>(host);
  10. }
  11.  
  12. @Override
  13. public void handleMessage(Message msg) {
  14. super.handleMessage(msg);
  15. if (mWeakRef.get() != null) {
  16. mWeakRef.get().invalidate();
  17. sendEmptyMessageDelayed(0, refreshPeriod);
  18. }
  19. }
  20. }

Handler学习小结的更多相关文章

  1. pthread多线程编程的学习小结

    pthread多线程编程的学习小结  pthread 同步3种方法: 1 mutex 2 条件变量 3 读写锁:支持多个线程同时读,或者一个线程写     程序员必上的开发者服务平台 —— DevSt ...

  2. ExtJs学习笔记之学习小结LoginDemo

    ExtJs学习小结LoginDemo 1.示例:(登录界面) <!DOCTYPE html> <html> <head> <meta charset=&quo ...

  3. clone的fork与pthread_create创建线程有何不同&pthread多线程编程的学习小结(转)

    进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合,这些资源在Linux中被抽 象成各种数据对象:进程控制块.虚存空间.文件系统,文件I/O.信号处理函数.所以创建一个进程的 过程就是这 ...

  4. flex学习小结

    接触到flex一个多月了,今天做一个学习小结.如果有知识错误或者意见不同的地方.欢迎交流指教. 画外音:先说一下,我是怎么接触到flex布局的.对于正在学习的童鞋们,我建议大家没事可以逛逛网站,看看人 ...

  5. Python 学习小结

    python 学习小结 python 简明教程 1.python 文件 #!/etc/bin/python #coding=utf-8 2.main()函数 if __name__ == '__mai ...

  6. react学习小结(生命周期- 实例化时期 - 存在期- 销毁时期)

    react学习小结   本文是我学习react的阶段性小结,如果看官你是react资深玩家,那么还请就此打住移步他处,如果你想给一些建议和指导,那么还请轻拍~ 目前团队内对react的使用非常普遍,之 ...

  7. objective-c基础教程——学习小结

    objective-c基础教程——学习小结   提纲: 简介 与C语言相比要注意的地方 objective-c高级特性 开发工具介绍(cocoa 工具包的功能,框架,源文件组织:XCode使用介绍) ...

  8. 点滴的积累---J2SE学习小结

    点滴的积累---J2SE学习小结 什么是J2SE J2SE就是Java2的标准版,主要用于桌面应用软件的编程:包括那些构成Java语言核心的类.比方:数据库连接.接口定义.输入/输出.网络编程. 学习 ...

  9. (转) Parameter estimation for text analysis 暨LDA学习小结

    Reading Note : Parameter estimation for text analysis 暨LDA学习小结 原文:http://www.xperseverance.net/blogs ...

随机推荐

  1. Programming In Scala笔记-第五章、Scala中的变量类型和操作

    这一章的一些基础性的东西,主要包括Scala中的基本变量类型,以及相关的一些操作符. 一.简单类型 下表中列出Scala语言中的基本类型,以及其字节长度,其中Byte, Short, Int, Lon ...

  2. 1、win10下连接本地系统上的Linux操作系统(分别以Nat方式和桥接模式实现)

    1.win10下连接本地系统上的Linux操作系统(分别以Nat方式和桥接模式实现) 一.准备知识:win10下打开Administrator的方式 在win10操作系统中,Administrator ...

  3. Python 文本转语音

    文本转语音,一般会用在无障碍开发.下面介绍如何使用Python实现将文本文件转换成语音输出. 准备 使用Speech API 原理 示例代码 小总结 pyttsx方式 原理 示例代码 小总结 pytt ...

  4. RE模块错误已解决.

    下面这个错误是由于在正则[...]的内部,减号'-'是一个有特殊含义的字符(代表字符范围) 所以如果需要在[...]内匹配减号'-',需要用反斜杠'\'转义. >>> import ...

  5. 使用MD5SUM检查文件

    有不少网站提供下载文件的同时,提供了文件的MD5SUM的值.如何检查自己下载的文件与原文件一样呢?用md5sum的-c选项. 操作如下: 1.先新建一个文本文件,写入网站上提供的md5sum的值,空两 ...

  6. 集合框架之Map接口

    Map是将键映射到值的对象.一个映射不能包含重复的键:每个键最多只能映射到一个值. Map 接口提供三种collection视图,允许以键集.值集或键-值映射关系集的形式查看某个映射的内容.映射顺序定 ...

  7. Android简易实战教程--第六话《开发一键锁屏应用2·完成》

    转载请注明出处:http://blog.csdn.net/qq_32059827/article/details/51885687点击打开链接 上一篇,初步开发了这个应用,功能都有了(见http:// ...

  8. 财务模块多组织,GL, SLA, SOB, COA, BSV, CCID, LE 概念的简单介绍

     GL=  General Ledger 指的是Oracle 的总帐系统. application_id = 101. 在uk似乎居然还有不同的解释(In the UK, it was refer ...

  9. 【UML 建模】在线UML建模工具 ProcessOn 使用详解

    总结 : -- 推荐理由 : 最近从 Windows 操作系统 转到 MAC 上, 正在看设计模式 和 重构, 找不到好用的 UML 工具, 因此在网上找了一款可以在线使用的 UML 工具, 用起来发 ...

  10. 服务端技术进阶(八)GitHub入门篇

    服务端技术进阶(八)GitHub入门篇 前言 在投递简历的过程中,发现有的公司会要求填写自己的GitHub地址,而自己却还没有GitHub帐号,准确点说是自己还不太会使用GitHub.(貌似开源社区中 ...