Handler和Looper详解
关于这个话题,其实很早之前就想撰写相关来的稳当来加加深映像,或者当做技术笔记,但是一直都不知道从哪里开始比较合适.先看看网上给出的相关的定.
Hanlder作用:
1)执行计划任务,你可以再预定的实现执行某些任务,可以模拟定时器
2)线程间通信。在Android的应用启动时,会创建一个主线程,主线程会创建一个消息队列来处理各种消息。当你创建子线程时,你可以再你的子线程中拿到父线程中创建的Handler对象,就可以通过该对象向父线程的消息队列发送消息了。由于Android要求在UI线程中更新界面,因此,可以通过该方法在其它线程中更新界面。
一、 角色描述
1.Looper:(相当于隧道) 一个线程可以产生一个Looper 对象,由它来管理此线程里的Message Queue( 车队,消息隧道) 。
2.Handler: 你可以构造Handler 对象来与Looper 沟通,以便push 新消息到Message Queue 里;或者接收Looper( 从Message Queue 取出) 所送来的消息。
3.Message Queue( 消息队列): 用来存放线程放入的消息。
4.线程:UI thread 通常就是main thread ,而Android 启动程序时会替它建立一个Message Queue 。
每一个线程里可含有一个Looper 对象以及一个MessageQueue 数据结构。在你的应用程序里,可以定义Handler 的子类别来接收Looper 所送出的消息。
二、 执行过程
当我们在子线程创建一个Handler的时候,目的就是就可以通过该对象向父线程的
消息队列发送消息了,那么这个handler对象是怎么实现的呢? 其实,当创建好一个Handler的时候,在Handler的构造方法中会获得一个主线程的消息队列监听器 —— Looper.这里简单说一下Looper,,我们可以理解它为一个隧道或者是一直循环的监听器,我们通常说的Looper都是主线程Looper,当主线程启动的时候,它会通过Looper的prepareMainLooper()方法,获得当前线程的Looper实例,然后让Looper调用loop方法不断地循环来处理message方法.看下面
代码
public static final void main(String[] args) {
SamplingProfilerIntegration.start();
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
好吧,可能这里很难理解,所以我们一点儿一点儿地来分析.然后再说handler怎么
发挥作用的.
先看 Looper.prepareMainLooper(),这里发生了什么?
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
if (Process.supportsProcesses()) {
myLooper().mQueue.mQuitAllowed = false;
}
}
看上面的代码可以知道,首先Looper的prepareMainLooper()方法中调用了 prepare()方法,这个方法作用是赋予当前线程一个新的Looper对象,看代码:
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created
per thread");
}
sThreadLocal.set(new Looper());
}
好了,明白了这个方法,那下一个方法setMainLooper(myLooper())呢? 继续看源代码!我们先来看myLooper()方法.
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
这个方法很简单,它的作用就是获得当前线程的Looper对象,前面我们刚刚赋予了 一个Looper对象给当前线程,这里便获取了它,是不是有种遥相呼应的感觉.. (再次声明,这里的当前线程指的是主线程或是UI线程),我们再来看看 setMainLooper()方法:
private synchronized static void setMainLooper(Looper looper) {
mMainLooper = looper;
}
也很简单,和之前的myLooper()方法就是把从当前线程获得Looper赋给一个mMainLooper的变量.
好了,总结一下,Looper.prepareMainLooper();的作用就是我们之前说的获得当前线程的Looper实例.下面再到 Looper.loop();看看
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local
process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
while (true) {
Message msg = queue.next(); // might block
//if (!me.mRun) {
// break;
//}
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit
message.
return;
}
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
msg.target.dispatchMessage(msg);
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf("Looper", "Thread identity changed from
0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while
dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycle();
}
}
}
这个方法看起来很复杂,其实我们只需要关注里面关键的几个点就行了.
1. Looper me = myLooper();刚才说过,就是获得当前线程(即主线程)的Looper.
2. MessageQueue queue = me.mQueue;这个2.MessageQueue在前面已经做了详细 的介绍,所以就不做解释了,但是突然出现在这里,有点莫名其妙......其实非也, 我们可以看到queue是来至于me.mQueue,就是Looper中的一个成员变量,这个 queue是什么时候产生的呢? 其实还记得刚才的prepare() 方法么.这个方法中 实例化了一个Looper——new Looper().queue就是来自于Looper的构造方法,请 看代码就会明白了.
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
所以不是莫名其妙,是有根据的.
3. Message msg = queue.next(),在循环体中看到的这段代码当然就是不断地将queue中的message一个一个地取出来啦!
4. msg.target.dispatchMessage(msg);经过之前一系列的判断处理之后,来到了loop()方法中非常核心的地方,这里作用就是把这个从queue中获得的message分发给相应的地方来进行处理.好了。。。说了半天,终于能回到我们开始说的Handler了.
当时Handler怎么做才能和之前的步骤产生联系呢? Handler的使用我们必须先实例化并实现它的handleMessage方法.呵呵,其实这个 只是一个引子......当我们实例化Handler的时候其实就是和之前说过的Looper还有什么queue,产生联系的时候,继续看代码:
public Handler(Callback callback) {
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());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not
called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
}
这下看Handler的构造方法是不是感觉很轻松?那就好.....之前那么长的铺垫没有白费,如果还不懂,再结合者前面的讲解看几遍.总结下,当实例化一个Handler的时候,这个handler会获得主线程中的Looper对象,和Looper中的成员变量mQueue,好了,所有的拼图碎片已经筹齐我们开始拼图了.我们平常实例化了Handler是不是就可以使用它的sendMessage方法呢?我们看看sendMessage到底干了什么,它其实调用了一个sendMessageAtTime()方法.
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
哦.... 相信大家已经能明白了.
首先msg.target = this;是不是很熟悉,我帮你们回忆一下.在Looper的looper的讲解中,第四个重点.
msg.target.dispatchMessage(msg)
是不是有种醍醐灌顶的感觉.这下所有的拼图组成一张图了,从这句核心代码就明白了,主线程Looper负责把所有的Message分发给相对应的handler来处理,然后把这个Message再次放入队列.而target就是指的Handler.最后指定的Handler的HandleMessage来处理这个消息,
这个就是整个Handler和Looper工作的流程....