Looper和Handler的分析

来源:互联网 发布:数据库产品有哪些 编辑:程序博客网 时间:2024/05/22 01:33

在写程序的时候我们经常用到handler来进行通信,但是却不太理解Handler的通信机制,今天有机会看了一下,下面来分析一下handler的机制。

看到Looper和handler就想起以前写程序的时候常用的几个方法Looper.prepare(),Looper.loop(),Handler的handleMessage(),就先从Looper.prepare()来分析:

以下是Looper.prepare()方法的源代码:

[java] view plaincopy
  1. private static void prepare(boolean quitAllowed) {  
  2.         if (sThreadLocal.get() != null) {  
  3.             throw new RuntimeException("Only one Looper may be created per thread");  
  4.         }  
  5.         sThreadLocal.set(new Looper(quitAllowed));  
  6.     }  

这个方法非常简单,但是其中有一个sThreadLocal,不知道它是做什么的,那我们来找一下它的定义:

[java] view plaincopy
  1. static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();  

在Looper中只是new了一个ThreadLocal的对象,这个ThreadLocal是什么呢?网上查了一下

[plain] view plaincopy
  1. 说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本。通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的。  
看了以下这个就明白了这段代码的作用了吧1:如果本线程中已经保存过Looper对象,就报了个“每个线程中只能有一个looper对象”的异常。2:创建一个Looper对象,并保存在ThreadLocal中。然后我们看一下Looper的构造方法:

[java] view plaincopy
  1. private Looper(boolean quitAllowed) {  
  2.      mQueue = new MessageQueue(quitAllowed);  
  3.      mRun = true;  
  4.      mThread = Thread.currentThread();  
  5.  }  

这个方法也很简单,只有两步1:创建一个MessageQueue对象。2:得到本线程的对象。

到目前为止Looper.prepare()方法已经执行完成,它都做了些什么呢?

1:创建一个Looper()对象,存入到ThreadLocal线程的局部变量中,每个线程只有一份。

2:创建了一个MessageQueue对象,用来保存消息

接着我们来分析一下Looper.loop()方法

[java] view plaincopy
  1. public static void loop() {  
  2.         final Looper me = myLooper();  
  3.         if (me == null) {  
  4.             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
  5.         }  
  6.         final MessageQueue queue = me.mQueue;  
  7.   
  8.         // Make sure the identity of this thread is that of the local process,  
  9.         // and keep track of what that identity token actually is.  
  10.         Binder.clearCallingIdentity();  
  11.         final long ident = Binder.clearCallingIdentity();  
  12.   
  13.         for (;;) {  
  14.             Message msg = queue.next(); // might block  
  15.             if (msg == null) {  
  16.                 // No message indicates that the message queue is quitting.  
  17.                 return;  
  18.             }  
  19.   
  20.             // This must be in a local variable, in case a UI event sets the logger  
  21.             Printer logging = me.mLogging;  
  22.             if (logging != null) {  
  23.                 logging.println(">>>>> Dispatching to " + msg.target + " " +  
  24.                         msg.callback + ": " + msg.what);  
  25.             }  
  26.   
  27.             msg.target.dispatchMessage(msg);  
  28.   
  29.             if (logging != null) {  
  30.                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);  
  31.             }  
  32.   
  33.             // Make sure that during the course of dispatching the  
  34.             // identity of the thread wasn't corrupted.  
  35.             final long newIdent = Binder.clearCallingIdentity();  
  36.             if (ident != newIdent) {  
  37.                 Log.wtf(TAG, "Thread identity changed from 0x"  
  38.                         + Long.toHexString(ident) + " to 0x"  
  39.                         + Long.toHexString(newIdent) + " while dispatching to "  
  40.                         + msg.target.getClass().getName() + " "  
  41.                         + msg.callback + " what=" + msg.what);  
  42.             }  
  43.   
  44.             msg.recycle();  
  45.         }  
  46.     }  

 这个方法中其实有用的信息也就几行,下面我们首先来看第一行
[java] view plaincopy
  1. final Looper me = myLooper()  

使用myLooper()方法得到了一个Looper()对象。其实就是从本线程的局部变量中将Looper对象拿出来。

[java] view plaincopy
  1. public static Looper myLooper() {  
  2.         return sThreadLocal.get();  
  3.     }  

得到Looper对象之后,然后又从Looper对象中拿出来MessageQueue.最终调用for循环来遍历MessageQueue,从消息队列中拿出来待处理的消息。

[java] view plaincopy
  1. msg.target.dispatchMessage(msg);  

然后我们再来看一下msg.target是什么?为什么要调用这个方法呢?在Message类的内部我们发现target的类型为Handler,所以这里应该是调用Handler的dispatchMessage()方法。

[java] view plaincopy
  1. public void dispatchMessage(Message msg) {  
  2.        if (msg.callback != null) {  
  3.            handleCallback(msg);  
  4.        } else {  
  5.            if (mCallback != null) {  
  6.                if (mCallback.handleMessage(msg)) {  
  7.                    return;  
  8.                }  
  9.            }  
  10.            handleMessage(msg);  
  11.        }  
  12.    }  
上面这段代码是Handler中的dispatchMessage()方法。可以看出在dispatchMessage()到处理流程。优先执行msg的回调方法,然后在执行handler的回调方法,以上方法都没有的话,最后执行handleMessage();看到handleMessage()方法是不是很熟悉呢?这handler类中找到handleMessage()方法,但是 他是一个空方法,需要我们自己实现,这就是我们写程序的时候最常用的handleMessage()方法了,来处理handler发送过来的消息。

消息的处理流程大概就是以上的分析,但是我们还是没有分析handler的sendMessage()方法,别着急,下面我们就来看一下Handler的内部结构。

[java] view plaincopy
  1. public Handler(Callback callback, boolean async) {  
  2.         if (FIND_POTENTIAL_LEAKS) {  
  3.             final Class<? extends Handler> klass = getClass();  
  4.             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  
  5.                     (klass.getModifiers() & Modifier.STATIC) == 0) {  
  6.                 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  
  7.                     klass.getCanonicalName());  
  8.             }  
  9.         }  
  10.   
  11.         mLooper = Looper.myLooper();  
  12.         if (mLooper == null) {  
  13.             throw new RuntimeException(  
  14.                 "Can't create handler inside thread that has not called Looper.prepare()");  
  15.         }  
  16.         mQueue = mLooper.mQueue;  
  17.         mCallback = callback;  
  18.         mAsynchronous = async;  
  19.     }  

这是Handler的构造方法,我们在其中来找到我们需要的信息,第一mLooper=Looper.myLooper(); 我们分析过Looper了,可知道这一行来得到线程局部变量ThreadLocal中保存的Looper对象,mQueue = mLooper.mQueue(); 这一句来得到Looper对象中的消息队列。

然后我们在分析Handler的sendMessage()方法。

[java] view plaincopy
  1. public final boolean sendMessage(Message msg)  
  2.     {  
  3.         return sendMessageDelayed(msg, 0);  
  4.     }  

然后我们接着往下找

[java] view plaincopy
  1. public final boolean sendMessageDelayed(Message msg, long delayMillis)  
  2.     {  
  3.         if (delayMillis < 0) {  
  4.             delayMillis = 0;  
  5.         }  
  6.         return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
  7.     }  
还是没有要的信息,那我们就在看一下sendMessageAtTime()方法。

[java] view plaincopy
  1. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  
  2.        MessageQueue queue = mQueue;  
  3.        if (queue == null) {  
  4.            RuntimeException e = new RuntimeException(  
  5.                    this + " sendMessageAtTime() called with no mQueue");  
  6.            Log.w("Looper", e.getMessage(), e);  
  7.            return false;  
  8.        }  
  9.        return enqueueMessage(queue, msg, uptimeMillis);  
  10.    }  

这里来判断从Looper对象中拿到的消息队列是不是null,如果不是null,就执行enqueueMessage方法,这个方法一定就是把handler发送的消息放入消息队列的方法了。

到此为止,Handler和Looper发送和处理消息的流程我们就简单的分析完了。简单的说它的工作流程分为几步:

1.handler向本线程的消息队列中放入要处理的消息

2.Looper循环读取消息队列的消息,然后分配给相应的handler处理,Handler为构造Message是传入的Handler

3.Handler的handleMessage方法来处理相应的消息

由以上分析可知,handler和Looper都是使用的同一个消息队列。只有执行过Looper.prepare()和Looper.loop()方法的线程才可以使用Handler来发送处理消息。我们知道UI线程是可以使用Handler来发送消息的,那UI主线程一定也执行过这两个方法了。我们来看一看代码:

[java] view plaincopy
  1. public static void main(String[] args) {  
  2.         SamplingProfilerIntegration.start();  
  3.   
  4.         // CloseGuard defaults to true and can be quite spammy.  We  
  5.         // disable it here, but selectively enable it later (via  
  6.         // StrictMode) on debug builds, but using DropBox, not logs.  
  7.         CloseGuard.setEnabled(false);  
  8.   
  9.         Environment.initForCurrentUser();  
  10.   
  11.         // Set the reporter for event logging in libcore  
  12.         EventLogger.setReporter(new EventLoggingReporter());  
  13.   
  14.         Security.addProvider(new AndroidKeyStoreProvider());  
  15.   
  16.         Process.setArgV0("<pre-initialized>");  
  17.   
  18.         Looper.prepareMainLooper();  
  19.   
  20.         ActivityThread thread = new ActivityThread();  
  21.         thread.attach(false);  
  22.   
  23.         if (sMainThreadHandler == null) {  
  24.             sMainThreadHandler = thread.getHandler();  
  25.         }  
  26.   
  27.         AsyncTask.init();  
  28.   
  29.         if (false) {  
  30.             Looper.myLooper().setMessageLogging(new  
  31.                     LogPrinter(Log.DEBUG, "ActivityThread"));  
  32.         }  
  33.   
  34.         Looper.loop();  
  35.   
  36.         throw new RuntimeException("Main thread loop unexpectedly exited");  
  37.     }  

只不过主线程中执行的是Looper.prepareMailLooper(),其实它和Looper.prepare()方法的作用是一样的。 
0 0
原创粉丝点击