浅析Android消息处理机制--Handler And Looper

本篇文章浅析android Handler、Looper、Message等之间的交互以及特别要注意的地方,在此做一记录,以备后续。

接下来,分别简要的介绍一下Handler、Looper、Message。

一、概要

1.Handler(简要的说:a.分发消息;b.异步处理)

There are two main uses for a Handler: 
  (1) to schedule messages and runnables to be executed as some point in the future;           
  (2)to enqueue an action to be performed on a different thread than your own.     

1)构造

具体的构造方法,再者就不阐述了,下面贴一段主要的构造源码:

public Handler(Callback callback, boolean async) {
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

接下里,看看Handler有哪些成员:

final MessageQueue mQueue ;
final Looper mLooper;
final Callback mCallback ;
final boolean mAsynchronous;

对比上文
首先,我们可以看到,在构造Handler的时候,我们可以在外部传进Callback回调处理,这块涉及到Handler的消息处理回调,后续会讲:

 /**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     */
    public interface Callback {
        public boolean handleMessage(Message msg);
    }

其次, 该Handler 分发的消息都是异步的Asychronous(这个是在Handler enque 一个message的时候判断设置的),这个将来在MessageQueue在Looper循环去除Message的时候会做一判断;

if (mAsynchronous) {
    msg.setAsynchronous( true);
}

此外,我们还看到有一个mLooper成员变量,其实查看源码,我们可以看到,Handler提供一个构造方法,可以让传进外边的Looper;

public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
  }

而前面的那个构造方法,我们可以看到其有调Looper.myLooper(),来关联一个Looper。

最后,我们还看到有一个mQueue成员变量,其类型是MessageQueue,而其赋值从上面的两个构造方法中也可以看出,是通过将Looper的mQueue赋值给它

mQueue = looper.mQueue;

同上述对Handler构造的简要描述,我们可以看到,Handler具有消息分发功能(持有MessageQueue,实际是Looper的)以及消息处理功能(持有Looper、mCallBack)的硬件条件。

2)消息处理

1.消息发送:
a.sendXXX系列:
send系列最终都会调到一个方法sendMessageAtTime;

/**
     * Enqueue a message into the message queue after all pending messages
     * before the absolute time (in milliseconds) <var>uptimeMillis</var> .
     * <b>The time -base is {@link android.os.SystemClock#uptimeMillis}. </b>
     * You will receive it in {@link #handleMessage}, in the thread attached
     * to this handler.
     *
     * @param uptimeMillis The absolute time at which the message should be
     *         delivered, using the
     *         {@link android.os.SystemClock#uptimeMillis} time -base.
     *        
     * @return Returns true if the message was successfully placed in to the
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    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);
    }

最终通过调用enqueueMessage加入到MessageQueue中;

b.postXXX系列:
post系列最终也是通过调用sendMessageAtTime方法,将消息加入到MessageQueue中的,
先看一个post系列的方法的声明:

public final boolean post(Runnable r);

我们看到其参数是一个Runnable,而sendMessageAtTime需要将一个Message加入到Queue中,因此,就需要个方法将Runnable转换成Message,
这个方法就是:

 private static Message getPostMessage (Runnable r) {
        Message m = Message. obtain();
        m.callback = r;
        return m;
     }

*这里记住一点就是:m.callback = r;m是Message(这里与Handler处理消息有关)

c.Message.sendToTarget系列:
Message有个成员变量target,其类型为Handler;
简单的来说,一个Message会关联分发其的Handler(通过Handler创建Message的时候),如当Handler调用obtain系列来创建一个Message的时候会关联Handler

 public static Message obtain(Handler h) {
        Message m = obtain();
        m. target = h;

        return m;
     }

其次,再调用Message的sendToTarget,通过Handler分发消息。
/**
* Sends this Message to the Handler specified by {@link #getTarget}.
* Throws a null pointer exception if this field has not been set.
*/
public void sendToTarget() {
target.sendMessage (this );//target的类型就是Handler
}
2.消息处理(回调):
这里主要涉及Handler的消息处理回调,即,Looper通过在loop()方法中调用

 msg.target.dispatchMessage(msg);//上文说过,msg.target是Handler类型

来进行分发消息,接下来,我们看看这个方法:

/**
     * Handle system messages here.
     */
    public void dispatchMessage (Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if ( mCallback != null) {
                if ( mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

如果Message具有callBack的话执行handlerCallback,是不是依稀记得上文在调用Handler的post发送消息的时候,将一个Runnable复制给Message的callBack,
下来,我们先看看handlerCallback这个方法:

private static void handleCallback (Message message) {
        message.callback.run();
    }

果然如此,不用多说了

接下里,如果Message 没有callback ,调用Handler自己的监听回调mCallBack的handleMessage方法,callBack是CallBack类型,其实通过在构造方法中传递进来的。

最后就是通过调用handleMessage方法来处理了,其是一个空方法,如果要处理,需要在Handler的子类中进行复写。

2.Looper

1)成员变量:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;  // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;

2)相关成员变量解释

 我们看到:
 sThreadLocal 保存线程相关变量,主要是给当前线程绑定Looper;
 sMainLooper: 主要是用于prepareMainLooper来进行设置;
 具体的应用,在android UI线程中设置,如ActivityThread中的main()方法:
 public static void main(String[] args) {
           ,,,,//此处有很多代码
          Looper.prepareMainLooper();
           ,,,,//此处有很多代码
          Looper.loop();
     }    

注意一点就是,prepareMainLooper,不一定就是将此Looper与UI线程关联,其调用如下:

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

我们可以看到,其最终还是调用myLooper来获取当前线程绑定的Looper对象,当然再次之前须调用一次prepare方法,注意prepare方法中
有个boolean参数:

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

再跟构造方法:

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

quitAllowed:消息处理是否允许退出。

当我们调用Looper的quit方法,就会调到Queue的quit方法。

可以看一下MessgaeQueue的quit方法

final void quit() {
        if (! mQuitAllowed) {
            throw new RuntimeException( "Main thread not allowed to quit.");
        }

        synchronized ( this) {
            if ( mQuiting) {
                return;
            }
            mQuiting = true;
        }
        nativeWake( mPtr);
    }

我们可以看到,通过该标识,来判断当前线程是否退出,注意:主线程不允许quit哦

3)消息处理分发:

 谈到Looper,不得不谈的一点就是Looper的loop函数,其是实现消息分发的核心之处:
 首先,其拥有一个while true循环(此Looper循环是有VM来控制的,Looper并没有另开启一个线程来专门做这个事情)
 其次,从MessgaeQueue中取一个Message;
 最后,调用Handler的dispatch..方法将消息交由Handler处理;
/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the 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;

        ,,,

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            ,,,//此处有很多代码

            msg.target.dispatchMessage(msg);

            ,,,//此处有很多代码

            msg.recycle();
        }
    }

从loop中我们可以看出,首先当前线程的Looper是不是存在,然后从MessageQueue中获取一个Message交由Handler处理;
最后将该Messgae回收。

在上面的loop中我们注意到,获取Message的时候,我们调用Queue的next方法,我们接下来,看看这个next方法:

final Message next() {

           ,,,//此处有很多代码

        for (;;) {

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

                ,,,//此处有很多代码

            }

            ,,,//此处有很多代码

        }
    }

在说这段代码之前,首先要说一下想MessageQueue中加入消息;

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) {

           ,,,

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

从上文我们可以看到,当将一个Messgae加入到Queue,是按时间的先后顺序来加入的(当然前提的是消息是同步的)
查看enqueueMessage,我们发现mMessages保存第一个加入到Queue中的有效的Message(何为有效,三点:1.target不为null;2.时可用的Message;3.发送消息的时间至少要小于一个现已存在Queue中Message)

好,明确Queue中的mMessages是保存第一个加入到Queue中的有效的Message之后,我们在回过头来看一下Queue的next()方法 经过一系列的过滤,调用

Message prevMsg = null;
    Message msg = mMessages;

     if (prevMsg != null) {
        prevMsg. next = msg.next ;
    } else {
        mMessages = msg.next ;
    }
    msg. next = null;
    if ( false) Log. v( "MessageQueue", "Returning message: " + msg);
    msg. markInUse();

取出第一个有效的Message。

3.Message

对于Message,主要说一点,即调用什么方法创建Message。
首先,Message有其构造方法,也就是说 ,我们可以通过new Message()来创建一个Message;
另外,Message提供了一个Obtain方法,如下

/**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain () {
        synchronized ( sPoolSync) {
            if ( sPool != null) {
                Message m = sPool;
                sPool = m. next;
                m. next = null;
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

此方法采用了缓冲,如上所说避免了创建新的对象,以节省资源。此方法与recycle方法互补调用;

/**
     * Return a Message instance to the global pool.  You MUST NOT touch
     * the Message after calling this function -- it has effectively been
     * freed.
     */
    public void recycle () {
        clearForRecycle();

        synchronized ( sPoolSync) {
            if ( sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

recycle方法实在Looper的loop处理函数中,当将一个消息处理完之后,就会调用其recycle,以便后续重用。

二、举例说明

1.Handler的创建

1)在UI线程中创建;

private void createHandlerInUIThread() {
           //调用无参构造方法,此时会在其构造方法中调用Looper.myLooper(),而主线程中已调用Looper.prepare(true),故可获取Looper
          Handler mainHandler = new Handler();
     }

Log:04-10 17:29:12.499: I/com.handler.test.HandlerActivity(4715): ———–>createHandlerInUIThread looper:Looper{41766448}
这个运行是可以的

2)在子线程中创建;

 private void createHandlerInSubThread() {
           new Thread( new Runnable() {

               @Override
               public void run() {
                    //调用无参构造方法,此时会在其构造方法中调用Looper.myLooper(),而此时子线程中没调用Looper.prepare(),故可不能获取Looper,会报错
                   Handler mainHandler = new Handler();
                   Log. i( TAG, "----------->createHandlerInSubThread looper:"+Looper.myLooper ());               
              }
          }).start();
     }
     Log:04-10 17:31:31.019: E/AndroidRuntime(4811): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

如果要解决这个问题,只需如在子线程创建Handler之前,调用Looper的prepare方法即可;

 private void createHandlerInSubThread() {
           new Thread( new Runnable() {

               @Override
               public void run() {
                    //调用Looper的 oprepare方法
                   Looper. prepare();
                   Handler mainHandler = new Handler();
                   Log. i( TAG, "----------->createHandlerInSubThread looper:"+Looper.myLooper ());   
                   Looper. loop(); //如果步调用这个方法,则不会处理消息
                        //如果把log放在这里是打印不出来的,因为loop是一个死循环,不会执行到下面的语句,也进一步说明loop并没有开启另一个线程来执行循环
                   //Log. i( TAG, "----------->createHandlerInSubThread looper:"+Looper.myLooper ());        
              }
          }).start();
     }
     Log:04-10 17:38:01.379: I/com.handler.test.HandlerActivity(5098): ----------->createHandlerInSubThread looper:Looper{417dd680}

1.在主线程中操作主线程的Handler改变UI
protected void interactiveMainHandlerAndSubLooper() {
Handler mainHandler = new Handler(loopers .get(“sub” ));//持有子线程的looper
Log. i( TAG, “———–>interactiveMainHandlerAndSubLooper looper:”+mainHandler.getLooper());
}
Log:
04-10 17:49:17.739: I/com.handler.test.HandlerActivity(5408): —————>onitemClick:view=16908308;position=0;id=0
04-10 17:49:17.739: I/com.handler.test.HandlerActivity(5408): ———–>createHandlerInUIThread looper:Looper{41762d40}
04-10 17:49:17.739: I/com.handler.test.HandlerActivity(5408): ———–>createHandlerInSubThread looper:Looper{417d6ae8}
04-10 17:49:19.819: I/com.handler.test.HandlerActivity(5408): —————>onitemClick:view=16908308;position=1;id=1
04-10 17:49:19.819: I/com.handler.test.HandlerActivity(5408): ———–>interactiveMainHandlerAndSubLooper looper:Looper{417d6ae8}
04-10 17:49:19.819: I/com.handler.test.HandlerActivity(5408): ———–>interacticeSubHandlerAndMainLooper looper:Looper{41762d40}
2.在主线程中操作子线程中拥有Looper的Handler来操作UI

 protected void interacticeSubHandlerAndMainLooper() {
           new Thread( new Runnable() {

               @Override
               public void run() {
                   Handler subHandler = new Handler(loopers .get("main" ));//持有子线程的looper
                   Log. i( TAG, "----------->interacticeSubHandlerAndMainLooper looper:"+subHandler.getLooper());
              }
          }).start();
     }

Log:
04-10 17:49:17.739: I/com.handler.test.HandlerActivity(5408): —————>onitemClick:view=16908308;position=0;id=0
04-10 17:49:17.739: I/com.handler.test.HandlerActivity(5408): ———–>createHandlerInUIThread looper:Looper{41762d40}
04-10 17:49:17.739: I/com.handler.test.HandlerActivity(5408): ———–>createHandlerInSubThread looper:Looper{417d6ae8}
04-10 17:49:19.819: I/com.handler.test.HandlerActivity(5408): —————>onitemClick:view=16908308;position=1;id=1
04-10 17:49:19.819: I/com.handler.test.HandlerActivity(5408): ———–>interactiveMainHandlerAndSubLooper looper:Looper{417d6ae8}
04-10 17:49:19.819: I/com.handler.test.HandlerActivity(5408): ———–>interacticeSubHandlerAndMainLooper looper:Looper{41762d40}
3.在主线程中操作主线程的Handler改变UI
这个请自行脑补;

4.在主线程中操作子线程中拥有Looper的Handler来操作UI
可以操作UI,但除了setContentView();否则会报非UI线程的错误;
Log: 04-10 17:58:57.879: E/AndroidRuntime(5617): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
5.在子线程中通过操作UI线程的Handler来改变UI
这个也很常用,请自行脑部;

6.在子线程中通过操作拥有Looper的子线程的Handler来操作UI;
可以操作UI,但除了setContentView();否则会报非UI线程的错误;
Log:04-10 17:58:57.879: E/AndroidRuntime(5617): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

另外,我在网上看到几篇写的不错的文章,将链接附上,有些图我就再者不画了:
1.http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html
2.http://android-developers.blogspot.hk/2007/11/stitch-in-time.html
3.http://www.apkbus.com/home.php?mod=space&do=blog&uid=623357&id=58615

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值