Android消息机制之 Looper、Handler、Message的关系和运行
2016-03-11 10:50
441 查看
在网上看了好多介绍这铁三角的文章,有所感悟,但是要想真正消化,还是要自己动手记录一下。首先推荐一篇文章:
http://www.cnblogs.com/codingmyworld/archive/2011/09/12/2174255.html
这篇文章基本上把我的理解讲完了,所以大家可以直接飞进去,看图文并茂哦。另外声明,文章引用源码代码块均转自上面推荐文章。
下面讲讲自己的理解,看完大牛的文章,总得能讲出自己的感悟才行呀。
主人公出场!
1、首先,Looper(为什么我一看见这个词,就想到了RNG的上单Looper!果然世界上最遥远的距离,是你在讲代码,我却理解成了LOL = =)我们可以称之为泵,循环者,balabala……它被设计用来使一个普通线程变成Looper线程,因为它的任务就是不停的循环体内的队列,取出消息,找出消息target,让它执行。我们形象一点,Looper就是外卖小哥,他的任务就是不停地拿出订单(队列),找到订单上的人(target),让此人拿走外卖(执行操作)。当我们需要让一个线程不断循环执行一系列有序操作时,就可以用到Looper,而你只需要敲两行代码:
我们需要知道,每个线程只能有一个Looper,即每个山头只能有一个大王。每个Looper都有一个队列,即每个大王麾下都有一批小喽啰。
上Looper源码:
2、Handler,有人称之为“异步处理大师”,它的引用可以在任何线程发送消息,并在new它的地方,处理自己的消息。形象的理解,就像我们披萨公司(Handler)在不同省市,有很披萨分店(引用),我们都属于披萨集团(Handler)。一旦某省声明,我们需要一种全新的披萨(消息),分店赶紧通知总公司(发送消息),总公司就加紧研发(获得消息并处理)。
我们需要知道,如果建立Handler的时候,没有指定Looper,那它默认绑定当前线程的Looper。Handler内心OS:我就是墙头草,咬我呀!
上源码:
Handler中的核心方法是,dispatchMessage(Message msg)和sendMessage(Message
msg)。后者由Handler调用(为啥先说后者,我,我任性),当我们调用handler.sendMessage(msg)的时候,首先,msg会把target指定为此handler,所以在Looper轮询消息队列,到此条msg的时候,才能找到执行者—handler。
前者由Looper调用,也就是在:
它的核心代码是:
看到这里,你明白为什么我要先说后者,再说前者了吧,哼(傲娇)!
3、Message自然就是任务了,可说的不多,但有两点让我大呼学到了,先看第一点:
我想象的Message是这样用:
再说第二点,出自最开始说的大牛文章,传送门在文章开头。
如果你的message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存,约翰福音2.0~
http://www.cnblogs.com/codingmyworld/archive/2011/09/12/2174255.html
这篇文章基本上把我的理解讲完了,所以大家可以直接飞进去,看图文并茂哦。另外声明,文章引用源码代码块均转自上面推荐文章。
下面讲讲自己的理解,看完大牛的文章,总得能讲出自己的感悟才行呀。
主人公出场!
1、首先,Looper(为什么我一看见这个词,就想到了RNG的上单Looper!果然世界上最遥远的距离,是你在讲代码,我却理解成了LOL = =)我们可以称之为泵,循环者,balabala……它被设计用来使一个普通线程变成Looper线程,因为它的任务就是不停的循环体内的队列,取出消息,找出消息target,让它执行。我们形象一点,Looper就是外卖小哥,他的任务就是不停地拿出订单(队列),找到订单上的人(target),让此人拿走外卖(执行操作)。当我们需要让一个线程不断循环执行一系列有序操作时,就可以用到Looper,而你只需要敲两行代码:
<span style="font-size:14px;">public class LooperThread extends Thread { @Override public void run() { // 将当前线程初始化为Looper线程 Looper.prepare(); // ...其他处理,如实例化handler // 开始循环处理消息队列 Looper.loop(); } }</span>轻松+愉快有木有!
我们需要知道,每个线程只能有一个Looper,即每个山头只能有一个大王。每个Looper都有一个队列,即每个大王麾下都有一批小喽啰。
上Looper源码:
<span style="font-size:14px;">public class Looper { // 每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象 private static final ThreadLocal sThreadLocal = new ThreadLocal(); // Looper内的消息队列 final MessageQueue mQueue; // 当前线程 Thread mThread; // 。。。其他属性 // 每个Looper对象中有它的消息队列,和它所属的线程 private Looper() { mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread(); } // 我们调用该方法会在调用线程的TLS中创建Looper对象 public static final void prepare() { if (sThreadLocal.get() != null) { // 试图在有Looper的线程中再次创建Looper将抛出异常 throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper()); } // 其他方法 }</span>可见Looper是绑定当前线程的。有了Looper线程,怎么往里面塞任务,且看Handler发威。
2、Handler,有人称之为“异步处理大师”,它的引用可以在任何线程发送消息,并在new它的地方,处理自己的消息。形象的理解,就像我们披萨公司(Handler)在不同省市,有很披萨分店(引用),我们都属于披萨集团(Handler)。一旦某省声明,我们需要一种全新的披萨(消息),分店赶紧通知总公司(发送消息),总公司就加紧研发(获得消息并处理)。
我们需要知道,如果建立Handler的时候,没有指定Looper,那它默认绑定当前线程的Looper。Handler内心OS:我就是墙头草,咬我呀!
上源码:
<span style="font-size:14px;">public class handler { final MessageQueue mQueue; // 关联的MQ final Looper mLooper; // 关联的looper final Callback mCallback; // 其他属性 public Handler() { // 没看懂,直接略过,,, if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } // 默认将关联当前线程的looper mLooper = Looper.myLooper(); // looper不能为空,即该默认的构造方法只能在looper线程中使用 if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } // 重要!!!直接把关联looper的MQ作为自己的MQ,因此它的消息将发送到关联looper的MQ上 mQueue = mLooper.mQueue; mCallback = null; } // 其他方法 }</span>可见Handler会直接把关联Looper的队列当作自己的,一有消息,就发送到该队列上,当真是没把自己当作外人!所以当我们在主线程New了一个Handler以后,整个“寄生体系”就建立了。
Handler中的核心方法是,dispatchMessage(Message msg)和sendMessage(Message
msg)。后者由Handler调用(为啥先说后者,我,我任性),当我们调用handler.sendMessage(msg)的时候,首先,msg会把target指定为此handler,所以在Looper轮询消息队列,到此条msg的时候,才能找到执行者—handler。
前者由Looper调用,也就是在:
<span style="font-size:14px;"> Looper.loop();</span>
它的核心代码是:
msg.target.dispatchMessage(msg);
看到这里,你明白为什么我要先说后者,再说前者了吧,哼(傲娇)!
3、Message自然就是任务了,可说的不多,但有两点让我大呼学到了,先看第一点:
我想象的Message是这样用:
<span style="font-size:14px;">Message msg = new Message(); msg.what = XX; msg.obj = XX;</span>而实际上,源码中的它是这样用:
<span style="font-size:14px;">Message msg = handler.obtainMessage(); msg.what = xxx; msg.obj = xxx;</span>因为调用obtainMessage方法,会先看消息池MessagePool里有没有空闲消息,有就复用,没有再new新的,这样可以节省内存,对于经常OOM的小内存机型来说,简直是约翰福音啊有木有!
再说第二点,出自最开始说的大牛文章,传送门在文章开头。
如果你的message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存,约翰福音2.0~
相关文章推荐
- Android实现开机自动运行程序
- layout_marginLeft和layout_marginStart
- 科大讯飞语音无限制录音、识别功能的实现:Android studio(一)
- Android开发中怎样调用系统Email发送邮件(多种调用方式)
- [置顶] android中viewPager双层嵌套问题,子viewpager无法滑动和滑动父级viewpager后子viewpager不显示内容
- Android开发常用命令整理
- Native Socket.IO and Android demo
- android Handle
- Android RecyclerView完全解析
- Android 短信监听
- android邮件发送几种方式
- Android Studio添加assets文件夹
- Android Menu的基本用法
- Android事件处理之基于监听的事件处理
- failed to find target with hash string 'android-23'
- android图片加载—二级缓存
- Android深入浅出系列之Socket—Socket编程(二)
- Android N分屏模式Activity生命周期的变化
- base64(Bitmap和base64的互换)
- Android项目开发(4)-忘记密码---验证码验证页面功能实现