Handler机制详解

在线程内部有一个或者多个Hadnler对象,外部程序通过该Handler对象向线程发送异步消息,消息经由Hadnler传递到MessageQueue对象中,线程内部只能包含一个MessageQueue对象,主线程执行函数中从MessageQueue中读取消息,并回调Handler对象中的函数handleMessage()。

为更好地理解Handler的工作原理,先介绍有Handler一起工作的几个逐渐:

Message:Handler接收和处理的消息对象。

Looper:每个线程只能拥有一个Looper,它的loop方法负责读取MessageQueue中的消息,读到消息之后就把消息交给该消息对应的Hadnler进行处理。

MessageQueue:消息队列,它采用先进的方式来管理Message,程序创建Looper对象时会在它的构造器中创建Looper对象。

下面是线程内部Handler、MessageQueue、Looper类的调用过程。

我们通过调用Looper类的静态方法prepare()为线程创建MessageQueue对象,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的类型是ThreadLocal,该类的作用是提供“线程局部存储”(在本线程内的任何对象保持一致),sThreadLocal对象会根据调用该prepare()函数的线程的id保存一个数据对象,这个数据对象就是所谓的“线程局部存储”对象,该对象是通过sThreadLocal的set()方法设置进去的,Looper类中保存的这个对象是一个Looper对象。

prepare()函数的第一行先用get()方法去获取该线程对应的Looper对象,如果已经有的话,那么出错(因为一个线程只能有一个Looper对象,因为一个异步线程只能有一个消息队列),如果没有,创建一个新的Looper对象。

Looper的作用有两个:为该类静态函数的额prepare()的线程创建一个消息队列,第二个是提供静态的loop()函数,使调用该函数的线程进行无限的循环,并从消息队列中读取消息。

下面是Looper()对象的源码:

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//创建一个消息队列
        mRun = true;
        mThread = Thread.currentThread();
    }

    public static void loop() {
        final Looper me = myLooper();//返回当前的Looper对象,通过sThreadLocal的get方法
        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();
<span style="white-space:pre">	</span>//进入无限循环
        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);//完成对该消息的处理,也就是说,消息的具体处理
                                           //实际上是由程序指定的,msg变量的类型是Message,
                                           //msg.target的类型是Handler
            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();                //每当处理完消息都需要调用,回收该Message对象占用
                                  //的系统资源,因为Message类内部使用了一个数据池保存Message                                  //对象,从而避免了不停地创建和删除Message类对象,因此每次///处理完消息都需要将该Message对象表明为空,以便该对象可以被重用
        }
    }

下面说说MessageQueue。

该消息队列采用排队的方式对消息进行处理,即先到先处理,但如果消息本身被指定了被处理的时间,那么必须得等到该时间。队列中的消息以链表的结构进行保存。Message内部有一个next变量,指向下一个消息。

MessageQueue中主要有两个函数“取出消息”和“添加消息”。

分别为函数next()和enquenceMessage().

next()函数

先调用nativePollOnce(mPtr,int time)是一个JNI函数,他的作用是从消息队列中取出一个消息。MessageQueue本身并没保存消息队列,真正的消息队列数据保存在JNI中的C

代码中。也就是说会在C中创建一个NativeMessageQueue,这就是nativePollOnce第一个参数为int型变量的意义,在C中,该变量被强制转化为一个NativeMessageQueue对象,在C环境中,如果消息队列中没有消息,将导致当前线程被挂起,如果有,则C代码中将把该消息赋值给Java环境中的mMessages变量。

接下来的这段代码被包含在synchronize(this)关键字中,this被用作取消息和写消息的锁,这部分仅仅判断所指定的执行时间是否到了,如果到了,就返回该消息,并将mMessage变量置空。如果还没到,则尝试读取下一个信息。

如果mMessage为空,说明C环境中的消息队列没有可以执行的消息了,因此,执行mPendingIdleHandlers列表中的“空闲回调函数”,我们可以在MessageQueue中注册一些“空闲回调函数”,从而当线程中没有消息可以去执行这些“空闲代码”

    final Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;

        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            nativePollOnce(mPtr, nextPollTimeoutMillis);

            synchronized (this) {
                if (mQuiting) {
                    return null;
                }

                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

下面是enquenceMessage(),

该函数分两步:

将参数msg赋值给mMessages.

调用nativeWake(mPtr),这也是一个JNI函数,其内部会将mMessage消息添加到C环境中的消息队列中,并且如果消息线程处在挂起状态,则唤醒该线程。

    final boolean enqueueMessage(Message msg, long when) {
        if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg + " This message is already in use.");
        }
        if (msg.target == null) {
            throw new AndroidRuntimeException("Message must have a target.");
        }

        boolean needWake;
        synchronized (this) {
            if (mQuiting) {
                RuntimeException e = new RuntimeException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            }

            msg.when = when;
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
        }
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }

下面是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) {           //次数说明必须在构造Handler对象之前执行Looper.prepare()操作
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

JNI简介:Java Native Interface,是Java的本地接口,所谓本地一般是指C/C++.当使用JAVA进行程序设计的时候,一般有一下几种情况需要C语言的协助

调用驱动,操作系统提供的驱动一般都是C接口,Java本身不具备操作这些驱动的能力

对于某些大量数据的处理磨矿,Java的执行效率可能远低于C,因此希望用C去完成。

某些功能模块,可能两者执行效率差不多,但是已经存在C代码了,不想用Java再次去写,只想利用已有的C代码。

时间: 2024-11-01 09:42:21

Handler机制详解的相关文章

android Handler机制详解

  简单运行图: 名词解析: Message(消息):定义了一个包含描述以及随意的数据对象可以被发送到Hanlder的消息,获得消息的最好方法是Message.obtain或者Handler.obtainMessage方法: MessageQueue (消息队列):是looper中的一个消息队列: Looper:用于使一个消息队列在线程中循环,线程中默认是没有消息队列循环的,创建方法demo:   classLooperThreadextendsThread{      publicHandle

AsyncTask机制详解

AsyncTask是Android提供的一个轻量级异步任务机制,使用AsyncTask可以方便的执行异步任务,并将结果更新到main thread.AsyncTask中是通过Handler机制来让work thread和main thread通信的.如果你对Handler还不了解的话,可以通过我的这篇博客来了解Android的Handler机制.Android 异步消息处理机制 在这篇文章中我们将了解AsyncTask的基本用法以及从源码的角度来分析AsyncTask机制,首先我们来了解下开发过

Android应用AsyncTask处理机制详解及源码分析

[工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果] 1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个知识点.前面我们分析了Handler异步机制原理(不了解的可以阅读我的<Android异步消息处理机制详解及源码分析>文章),这里继续分析Android的另一个异步机制AsyncTask的原理. 当使用线程和Handler组合实现异步处理时,当每次执行耗时操作都创建一条新线程进行处理,性能开销会比

好程序员Java教程Java动态代理机制详解

好程序员Java教程Java动态代理机制详解:在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface).另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的.首先我们先来看看java的API帮助文档是怎么样对这两个类进行描述的: InvocationHandler: 1InvocationHandler is the interface implemented by the invocation handle

java异常处理机制详解

java异常处理机制详解 程序很难做到完美,不免有各种各样的异常.比如程序本身有bug,比如程序打印时打印机没有纸了,比如内存不足.为了解决这些异常,我们需要知道异常发生的原因.对于一些常见的异常,我们还可以提供一定的应对预案.C语言中的异常处理是简单的通过函数返回值来实现的,但返回值代表的含义往往是由惯例决定的.程序员需要查询大量的资料,才可能找到一个模糊的原因.面向对象语言,比如C++, Java, Python往往有更加复杂的异常处理机制.这里讨论Java中的异常处理机制. 异常处理 Ja

Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇)

1 背景 还记得前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>中关于透过源码继续进阶实例验证模块中存在的点击Button却触发了LinearLayout的事件疑惑吗?当时说了,在那一篇咱们只讨论View的触摸事件派发机制,这个疑惑留在了这一篇解释,也就是ViewGroup的事件派发机制. PS:阅读本篇前建议先查看前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>,这一篇承接上一篇. 关于View与ViewGroup的区别在前一篇的A

【Hibernate步步为营】--锁机制详解

上篇文章详细讨论了hql的各种查询方法,在讨论过程中写了代码示例,hql的查询方法类似于sql,查询的方法比较简单,有sql基础的开发人员在使用hql时就会变得相当的简单.Hibernate在操作数据库的同时也提供了对数据库操作的限制方法,这种方法被称为锁机制,Hibernate提供的锁分为两种一种是乐观锁,另外一种是悲观锁.通过使用锁能够控制数据库的并发性操作,限制用户对数据库的并发性的操作. 一.锁简介 锁能控制数据库的并发操作,通过使用锁来控制数据库的并发操作,Hibernate提供了两种

浏览器缓存机制详解

对于浏览器缓存,相信很多开发者对它真的是又爱又恨.一方面极大地提升了用户体验,而另一方面有时会因为读取了缓存而展示了"错误"的东西,而在开发过程中千方百计地想把缓存禁掉.那么浏览器缓存究竟是个什么样的神奇玩意呢? 什么是浏览器缓存: 简单来说,浏览器缓存就是把一个已经请求过的Web资源(如html页面,图片,js,数据等)拷贝一份副本储存在浏览器中.缓存会根据进来的请求保存输出内容的副本.当下一个请求来到的时候,如果是相同的URL,缓存会根据缓存机制决定是直接使用副本响应访问请求,还是

Android触摸屏事件派发机制详解与源码分析

请看下面三篇博客,思路还是蛮清晰的,不过还是没写自定义控件系列哥们的思路清晰: Android触摸屏事件派发机制详解与源码分析一(View篇) http://blog.csdn.net/yanbober/article/details/45887547 Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇) http://blog.csdn.net/yanbober/article/details/45912661 Android触摸屏事件派发机制详解与源码分析三(Activi