Android Handler机制

1.    四个部分:

1)   Message:在不同线程之间传递消息,可以在内部携带少量的信息。

2)   Handler:主要用于发送处理消息的。发送消息:sendMessage()方法,处理消息:handlerMessage()方法

3)   MesssageQueue:消息队列,主要用于存放所有通过Handler发送的消息。每个线程都只有一个MessageQueue对象。

4)   Looper:是每个线程的MesssageQueue的管家,调用Looper的loop方法后,就会进入到一个无限循环当中,每当发现MessageQueue中存在一个消息,就会将它取出,并传递到Handler中的handleMessage()方法中。每个线程中只会有一个Looper对象。

2.   Handler类中的重要方法

1)   handlerMessage()方法:在Handler类此方法为空方法,子类需要实现该方法,实现处理接收到的消息的具体的逻辑。

<span style="font-size:18px;">/**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }</span>

2)   dispatchMessage ()方法在Looper类中的loop方法中会回调此方法

<span style="font-size:18px;"> msg.target.dispatchMessage(msg);</span>

msg为Message类的对象,它的成员变量target实际上就是一个Handler对象

<span style="font-size:18px;">/*package*/ Handler target;</span>

再看dispatchMessage方法的实现:

<span style="font-size:18px;">   public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
           <span style="color:#ff0000;"> handleMessage(msg);</span>
        }
    }</span>


着重看我们用红色圈出的代码,就是调用了我们前面所说的用来实现具体的处理消息的逻辑。

3)   publicHandler(Callback callback, boolean async)

<span style="color:#0070c0;">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());
            }
        }

        </span><span style="color:#ff0000;">mLooper = Looper.myLooper();</span><span style="color:#0070c0;">
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        </span><span style="color:#ff0000;">mQueue = mLooper.mQueue;</span><span style="color:#0070c0;">
        mCallback = callback;
        mAsynchronous = async;
    }</span>


接着看这段代码,同样只关注红笔圈出的代码,实际上分别给我们的Handler中的Looper实例和MessageQueue实例赋值,这样,Handler就与Looper和MessageQueue建立了联系。(因为利用Handler发送消息时要和MessageQueue打交道,而从消息队列中取出消息时Handler需要和Looper打交道)

4)   sendMessage()方法

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

可以对这段代码一直向下追踪,最后这里追踪到这段代码停下来:

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        <span style="color:#ff0000;">msg.target = this;</span>
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        <span style="color:#ff0000;">return queue.enqueueMessage(msg, uptimeMillis);</span>
    }

有两行代码值得我们去关注

a.    第一个将msg.target赋值为this,还记得我们前面所说的这个target就是一个Handler类对象,在Looper类的loop方法中会调用这个target的dispatchMessage()方法。原来是在这里建立了联系,然后才能够在loop方法中去调用到dispatchMessage方法。

b.   调用了queue.enqueueMessage()方法。首先必须注意,此时已经跳出了Handler类的职能,也就是说调用的不再是Handler类中的方法,而是MessageQueue中的方法。这里的queue,实际上就是我们前面在Handler的构造方法中提到和Handler类建立联系的MessageQueue(不信可以返回到上一层函数调用)。到此,我们的分析流程就跳到MessageQueue的enqueueMessage()方法。下面就介绍MessageQueue类中的重要的方法。

3.   MessageQueue

1)   enqueueMessage()方法---添加消息

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

        synchronized (this) {
           <span style="color:#ff0000;"> if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                msg.recycle();
                return false;
            }</span>

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            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;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
               <span style="color:#ff0000;"> nativeWake(mPtr);</span>
            }
        }
        return true;
    }

咱们只关注synchronized中的代码。

a.     

如果mQuitting为true,则会进入这个条件语句,再注意红笔圈出的代码,从函数看出,这就是在回收消息。因为实际上底层维护了一个消息池,使用结束后要将资源归还到池中。

b.    

这段代码实际上是在执行将我们的消息插入到MessageQueue中。(实际上MessageQueue本身并不维护这个消息队列,注意紧跟这段代码之后的nativeWake()方法,这是一个JNI函数,其内部会将mMessages消息添加到C环境中的消息队列中)。

2)   next()-----取出消息

nativePollOnce(ptr, nextPollTimeoutMillis);

这里注意这样一行代码就行了,同样这里是一个JNI函数,其作用就是从消息队列中取出一个消息。

4.   Looper

1)   Looper的私有构造方法:

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

这个构造方法是用private关键字修饰的,意味着我们是无法通过构造方法new 出一个实例的。稍微有点Java经验的开发人员很快就会意识到这里面使用了单例模式,保证一个Thread只对应一个Looper对象。

2)   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));
    }


可以看到,在prepare()方法中new出了一个Looper实例,并将它设置到线程局部存储变量中。对于线程局部存储变量感兴趣的读者可自行查阅相关资料,这里我就不说明,再说我也说不好。再追踪到sThreadLocal.set()方法中:

下面是Thread.currentThread()的实现:

 public void set(T value) {
      <span style="color:#ff0000;">  Thread currentThread = Thread.currentThread();</span>
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }
查看它的注释:返回调用者线程,即当前线程。

那么,到这里,我们是不是能够得出一个结论:线程和我们的Looper对象是在这里建立联系的。

3)   loop()方法

    /** Returns the application's main looper, which lives in the main thread of the application.
     */
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        <span style="color:#ff0000;">final Looper me = myLooper();</span>
        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();
        }
    }


注意红笔圈出的代码,这里调用myLooper()方法返回当前的线程的Looper对象,其实现代码如下:

/**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }


接着进入如下这段for循环

for (;;) {
        <span style="color:#ff0000;">    Message msg = queue.next(); // might block</span>
            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);
            }

           <span style="color:#ff0000;"> msg.target.dispatchMessage(msg);</span>

            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();
        }


看到没有,在这里调用了我们前面在MessageQueue中分析的next()方法,即在此处取出消息,注意旁边的注释,可能会阻塞。实际上此时如果当前队列为空,则当前线程会被挂起。好!取出了消息,接下来的是不是应该去处理啊!把目光投到第二处红笔圈出的代码,是不是似曾相识?实际上我们在前面介绍Handler类时就截取过这段代码,这里应该不用我作过多解释了吧。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值