欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Handler信息机制详情

程序员文章站 2022-05-14 17:53:13
...
2015年05月13日 19:19:26
阅读数:788

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()函数的代码如下:

  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. }  
    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()对象的源码:

  1. private Looper(boolean quitAllowed) {  
  2.     mQueue = new MessageQueue(quitAllowed);//创建一个消息队列  
  3.     mRun = true;  
  4.     mThread = Thread.currentThread();  
  5. }  
  6.   
  7.   
  8. public static void loop() {  
  9.     final Looper me = myLooper();//返回当前的Looper对象,通过sThreadLocal的get方法  
  10.     if (me == null) {  
  11.         throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
  12.     }  
  13.     final MessageQueue queue = me.mQueue;//获得当前队列  
  14.   
  15.     // Make sure the identity of this thread is that of the local process,  
  16.     // and keep track of what that identity token actually is.  
  17.     Binder.clearCallingIdentity();  
  18.     final long ident = Binder.clearCallingIdentity();  
  19. span style="white-space:pre">    </span>//进入无限循环  
  20.     for (;;) {  
  21.         Message msg = queue.next(); // might block  
  22.         if (msg == null) {           //如果当前队列为空,线程会被挂起  
  23.             // No message indicates that the message queue is quitting.  
  24.             return;  
  25.         }  
  26.   
  27.         // This must be in a local variable, in case a UI event sets the logger  
  28.         Printer logging = me.mLogging;  
  29.         if (logging != null) {  
  30.             logging.println(">>>>> Dispatching to " + msg.target + " " +  
  31.                     msg.callback + ": " + msg.what);  
  32.         }  
  33.   
  34.         msg.target.dispatchMessage(msg);//完成对该消息的处理,也就是说,消息的具体处理  
  35.                                        //实际上是由程序指定的,msg变量的类型是Message,  
    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,
  1.                                //msg.target的类型是Handler  
  2. if (logging != null) {  
  3.     logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);  
  4. }  
  5.   
  6. // Make sure that during the course of dispatching the  
  7. // identity of the thread wasn't corrupted.  
  8. final long newIdent = Binder.clearCallingIdentity();  
  9. if (ident != newIdent) {  
  10.     Log.wtf(TAG, "Thread identity changed from 0x"  
  11.             + Long.toHexString(ident) + " to 0x"  
  12.             + Long.toHexString(newIdent) + " while dispatching to "  
  13.             + msg.target.getClass().getName() + " "  
  14.             + msg.callback + " what=" + msg.what);  
  15. }  
  16.   
  17. msg.recycle();                //每当处理完消息都需要调用,回收该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对象占用
  1.                               //的系统资源,因为Message类内部使用了一个数据池保存Message                                  //对象,从而避免了不停地创建和删除Message类对象,因此每次///处理完消息都需要将该Message对象表明为空,以便该对象可以被重用  
  2.     }  
  3. }  
                                  //的系统资源,因为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中注册一些“空闲回调函数”,从而当线程中没有消息可以去执行这些“空闲代码”

  1. final Message next() {  
  2.     int pendingIdleHandlerCount = -1// -1 only during first iteration  
  3.     int nextPollTimeoutMillis = 0;  
  4.   
  5.     for (;;) {  
  6.         if (nextPollTimeoutMillis != 0) {  
  7.             Binder.flushPendingCommands();  
  8.         }  
  9.         nativePollOnce(mPtr, nextPollTimeoutMillis);  
  10.   
  11.         synchronized (this) {  
  12.             if (mQuiting) {  
  13.                 return null;  
  14.             }  
  15.   
  16.             // Try to retrieve the next message.  Return if found.  
  17.             final long now = SystemClock.uptimeMillis();  
  18.             Message prevMsg = null;  
  19.             Message msg = mMessages;  
  20.             if (msg != null && msg.target == null) {  
  21.                 // Stalled by a barrier.  Find the next asynchronous message in the queue.  
  22.                 do {  
  23.                     prevMsg = msg;  
  24.                     msg = msg.next;  
  25.                 } while (msg != null && !msg.isAsynchronous());  
  26.             }  
  27.             if (msg != null) {  
  28.                 if (now < msg.when) {  
  29.                     // Next message is not ready.  Set a timeout to wake up when it is ready.  
  30.                     nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);  
  31.                 } else {  
  32.                     // Got a message.  
  33.                     mBlocked = false;  
  34.                     if (prevMsg != null) {  
  35.                         prevMsg.next = msg.next;  
  36.                     } else {  
  37.                         mMessages = msg.next;  
  38.                     }  
  39.                     msg.next = null;  
  40.                     if (false) Log.v("MessageQueue""Returning message: " + msg);  
  41.                     msg.markInUse();  
  42.                     return msg;  
  43.                 }  
  44.             } else {  
  45.                 // No more messages.  
  46.                 nextPollTimeoutMillis = -1;  
  47.             }  
  48.   
  49.             // If first time idle, then get the number of idlers to run.  
  50.             // Idle handles only run if the queue is empty or if the first message  
  51.             // in the queue (possibly a barrier) is due to be handled in the future.  
  52.             if (pendingIdleHandlerCount < 0  
  53.                     && (mMessages == null || now < mMessages.when)) {  
  54.                 pendingIdleHandlerCount = mIdleHandlers.size();  
  55.             }  
  56.             if (pendingIdleHandlerCount <= 0) {  
  57.                 // No idle handlers to run.  Loop and wait some more.  
  58.                 mBlocked = true;  
  59.                 continue;  
  60.             }  
  61.   
  62.             if (mPendingIdleHandlers == null) {  
  63.                 mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];  
  64.             }  
  65.             mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);  
  66.         }  
  67.   
  68.         // Run the idle handlers.  
  69.         // We only ever reach this code block during the first iteration.  
  70.         for (int i = 0; i < pendingIdleHandlerCount; i++) {  
  71.             final IdleHandler idler = mPendingIdleHandlers[i];  
  72.             mPendingIdleHandlers[i] = null// release the reference to the handler  
  73.   
  74.             boolean keep = false;  
  75.             try {  
  76.                 keep = idler.queueIdle();  
  77.             } catch (Throwable t) {  
  78.                 Log.wtf("MessageQueue""IdleHandler threw exception", t);  
  79.             }  
  80.   
  81.             if (!keep) {  
  82.                 synchronized (this) {  
  83.                     mIdleHandlers.remove(idler);  
  84.                 }  
  85.             }  
  86.         }  
  87.   
  88.         // Reset the idle handler count to 0 so we do not run them again.  
  89.         pendingIdleHandlerCount = 0;  
  90.   
  91.         // While calling an idle handler, a new message could have been delivered  
  92.         // so go back and look again for a pending message without waiting.  
  93.         nextPollTimeoutMillis = 0;  
  94.     }  
  95. }  
    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环境中的消息队列中,并且如果消息线程处在挂起状态,则唤醒该线程。

  1. final boolean enqueueMessage(Message msg, long when) {  
  2.     if (msg.isInUse()) {  
  3.         throw new AndroidRuntimeException(msg + " This message is already in use.");  
  4.     }  
  5.     if (msg.target == null) {  
  6.         throw new AndroidRuntimeException("Message must have a target.");  
  7.     }  
  8.   
  9.     boolean needWake;  
  10.     synchronized (this) {  
  11.         if (mQuiting) {  
  12.             RuntimeException e = new RuntimeException(  
  13.                     msg.target + " sending message to a Handler on a dead thread");  
  14.             Log.w("MessageQueue", e.getMessage(), e);  
  15.             return false;  
  16.         }  
  17.   
  18.         msg.when = when;  
  19.         Message p = mMessages;  
  20.         if (p == null || when == 0 || when < p.when) {  
  21.             // New head, wake up the event queue if blocked.  
  22.             msg.next = p;  
  23.             mMessages = msg;  
  24.             needWake = mBlocked;  
  25.         } else {  
  26.             // Inserted within the middle of the queue.  Usually we don't have to wake  
  27.             // up the event queue unless there is a barrier at the head of the queue  
  28.             // and the message is the earliest asynchronous message in the queue.  
  29.             needWake = mBlocked && p.target == null && msg.isAsynchronous();  
  30.             Message prev;  
  31.             for (;;) {  
  32.                 prev = p;  
  33.                 p = p.next;  
  34.                 if (p == null || when < p.when) {  
  35.                     break;  
  36.                 }  
  37.                 if (needWake && p.isAsynchronous()) {  
  38.                     needWake = false;  
  39.                 }  
  40.             }  
  41.             msg.next = p; // invariant: p == prev.next  
  42.             prev.next = msg;  
  43.         }  
  44.     }  
  45.     if (needWake) {  
  46.         nativeWake(mPtr);  
  47.     }  
  48.     return true;  
  49. }  
    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的构造函数。
  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) {           //次数说明必须在构造Handler对象之前执行Looper.prepare()操作  
  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. }  
    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代码。


相关标签: handler