[置顶] Handler机制详解

        分析完了 Handler,Message以及Looper类的源码以后,再来看看这些类是怎么实现一起协同工作的。我将一个消息从发送到执行分成了四个步骤----①Looper和MessageQueue的创建②Looper开始工作③Handler和Message的初始化以及发送消息到消息队列④执行消息

Looper和MessageQueue的创建

        如果需要在某一个线程中使用消息机制,首先需要创建一个Looper对象和MessageQueue对象。在一个线程中创建Looper和MessageQueue需要调用Looper类的prepare()实例方法。这个方法会去调用Looper的构造函数,在构造函数中实例化 MessageQueue。并将Looper对象放入到ThreadLocal变量中,实现Looper和此线程的绑定。如果我们要在UI线程中使用消息机制,也无可避免要初始化Looper和MessageQueue,只不过这部分工作由Android系统调用Looper类的prepareMainLooper()方法完成。此方法只能由Android系统调用。这部分的源代码如下:
 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));
    }


 public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

Looper开始工作

        为了让Looper开始工作,我们会调用Looper.loop方法。开始工作以后,Looper会进入无限循环,从MessageQueue里面取出Message,然后调用Message的target属性的dispatchMessage方法处理消息。值得注意的是:有的时候由于MessageQueue里面并没有消息,此时,Loop方法会阻塞,进入睡眠状态,当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.recycleUnchecked();
        }
    }

Handler和Message的初始化以及发送消息到消息队列

        创建一个带发送的Message,我们经常调用 Message.obtain()方法或者直接调用Message的构造。Android建议我们使用第一种方式,因为这样会让Message实现复用,避免每次分配内存,回收内存增加GC的负担。
       Handler的实例化,我们经常调用其构造然后重写handleMessage方法。值得注意的是:这里很容易出现内存泄露的情况。通过hanler.sendMessage或者其他方法将Message送入到MessageQueue中。进入到message以后,就激活了我们的第二步,Looper取出消息。准备执行。

处理消息

         当Looper拿到从MessageQueue取出的消息以后,就会调用handler的dispatchMessage方法,handleMessage方 法会首先调用handleCallBack()方法,如果没有callBack就会调用Handler类的CallBack的实现类或者调用我们重写的handleMessage方法。实现消息的处理。代码如下:
 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
          完成以上四步,就可以实现消息机制。在MessageQueue类中有很多native方法,这部分方法连接了java层的代码和底层的C代码。这部分代码构成了消息机制的底层基础。
       这四个部分贯穿了消息处理等始终。还有一些细节性的问题:
1,为什么要在Looper里使用ThreadLocal?
      这个变量将Looper对象和本线程进行绑定。ThreadLocal内部维护一个Map集合。key为本线程。value是我们想要保存的那个值。关于TheadLocal的详细解析,请戳这里。Looper内维护一个ThreadLocal,就是让创建Looper的线程与此Looper绑定。由于MessageQueue是在Looper初始化的方法创建的,此后,我们就可以往消息队列发送消息,这样说来,消息队列和此线程也是绑定的。
2,Message里面的target是Handler,这个变量是在哪里赋值的?Handler的handleMessage()方法在哪里调用的?
      Message对象的target是在Handler的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);
    }


private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
至于handleMessage在什么时候调用,就要结合第二步和第四步,Looper得到消息队列里面的一个Message会调用message.target.disPatchMessage方法,在dispatchMessage方法里面调用我们重写的handleMessage方法。







你可能感兴趣的:(android,Handler详解)