在网上看了好多介绍这铁三角的文章,有所感悟,但是要想真正消化,还是要自己动手记录一下。首先推荐一篇文章:
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~