Looper和Handler的分析

Posted zhenjie_chang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Looper和Handler的分析相关的知识,希望对你有一定的参考价值。

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

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

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

private static void prepare(boolean quitAllowed) 
        if (sThreadLocal.get() != null) 
            throw new RuntimeException("Only one Looper may be created per thread");
        
        sThreadLocal.set(new Looper(quitAllowed));
    

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

 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

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

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

   private Looper(boolean quitAllowed) 
        mQueue = new MessageQueue(quitAllowed);
        mRun = true;
        mThread = Thread.currentThread();
    

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

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

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

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

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

public static void loop() 
        final Looper me = myLooper();
        if (me == null) 
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        
        final 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();

        for (;;) 
            Message msg = queue.next(); // might block
            if (msg == null) 
                // No message indicates that the message queue is quitting.
                return;
            

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) 
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            

            msg.target.dispatchMessage(msg);

            if (logging != null) 
                logging.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(TAG, "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();
        
    

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

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

public static Looper myLooper() 
        return sThreadLocal.get();
    

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

 msg.target.dispatchMessage(msg);

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

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

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

public Handler(Callback callback, boolean async) 
        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;
        mAsynchronous = async;
    

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

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

public final boolean sendMessage(Message msg)
    
        return sendMessageDelayed(msg, 0);
    

然后我们接着往下找

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    
        if (delayMillis < 0) 
            delayMillis = 0;
        
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    
还是没有要的信息,那我们就在看一下sendMessageAtTime()方法。

 public boolean sendMessageAtTime(Message msg, long uptimeMillis) 
        MessageQueue queue = mQueue;
        if (queue == null) 
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        
        return enqueueMessage(queue, msg, uptimeMillis);
    

这里来判断从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主线程一定也执行过这两个方法了。我们来看一看代码:

public static void main(String[] args) 
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        Security.addProvider(new androidKeyStoreProvider());

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) 
            sMainThreadHandler = thread.getHandler();
        

        AsyncTask.init();

        if (false) 
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        

        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    

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




以上是关于Looper和Handler的分析的主要内容,如果未能解决你的问题,请参考以下文章

android的消息处理机制(图+源码分析)——Looper,Handler,Message

android的消息处理机制(图+源码分析)——Looper,Handler,Message

Android消息机制和原理

Android的Handler,Looper源码剖析

[Android源代码分析]Android消息机制,Handler,Message,Looper,MessageQueue

Android多线程分析之三:Handler,Looper的实现