Handler机制详解

来源:互联网 发布:法国工资 知乎 编辑:程序博客网 时间:2024/06/05 20:27


在线程内部有一个或者多个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代码。


0 0
原创粉丝点击