Handler机制

Android应用在启动的时候会开启一条主线程,也叫UI线程,在UI线程里面不能执行一些耗时操作,不然的话会使UI失去响应,会出现ANR。所以我们执行一些耗时操作(比如下载)的话需要另开子线程来执行,执行完成后可能需要更新UI(比如将下载下来的信息显示出来),但是Android只允许在主线程里面更新UI,因为UI线程是线程不安全的,这应该就是Handler机制产生的原因。
Looper、Handler、MessageQueue、

public static final void prepare() {  
        if (sThreadLocal.get() != null) {  
            throw new RuntimeException("Only one Looper may be created per thread");  
        }  
        sThreadLocal.set(new Looper(true));  
}  

Looper.prepare()方法会判断ThreadLocal是否已经保存了Looper,是则抛出异常,这说明,一个线程只能有一个Looper(),为什么说一个线程只有一个Looper(),这个只有弄清楚了ThreadLocal的工作原理才能明白。
简单说一下就是:

sThreadLocal.set(new Looper(true))//其实等同于以下
Thread.currentThread.ThreadLocalMap<ThreadLocal, Object>.put(sThreadLocal, Looper);

总结一下prepare()做的事情就是:判断当前线程是否已经有Looper了,有的话,抛出异常,没有的话,实例化一个Looper添加到ThreadLocal中,添加到ThreadLocal中,该Looper就和当前线程关联了。
对于ThreadLocal的理解可以参考http://blog.csdn.net/actor1999/article/details/52121303

获取Looper的方法,可以看到这个方法静态的。

 public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

Looper的构造方法,可以看到实例化了一个MessageQueue

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

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;  

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

可以看到,loop方法会执行一个死循环,从Message中取出数据 Message msg = queue.next(); 该方法可能会阻塞。当有Message的时候,调用msg.target.dispatchMessage(msg),这里的target是一个Handler对象,查看源码可以发现,该方法最终会调用handlerMessage()方法,而这个方法是个空方法,这也是我们处理消息的地方

 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

我们在调用Handler.sendMessage(Message msg)其他方法也一样,最终都是调用以下这个方法:

public boolean sendMessageAtTime(Message msg, long uptimeMillis)  
{  
    boolean sent = false;  
    MessageQueue queue = mQueue;  
    if (queue != null) {  
        msg.target = this;  
        sent = queue.enqueueMessage(msg, uptimeMillis);  
    }  
    else {  
        RuntimeException e = new RuntimeException(  
            this + " sendMessageAtTime() called with no mQueue");  
        Log.w("Looper", e.getMessage(), e);  
    }  
    return sent;  
}  

可以看到这个方法就是设置msg.target = this.
然后调用queue.enqueueMessage(msg, uptimeMillsi),这个方法其实就是将消息加入队列中。
Handler在实例化的时候,会保存当前线程的Looper,和该Looper的MeesageQueue,因此在实例化Handler的时候要先调用Looper.prepare();

为什么Looper要这么设计,一个线程只能有一个Looper,并且其构造函数是private的,我们从他出现的背景就能够领悟一二,文章开头说过了,他出现的背景是,子线程想更新UI,但是子线程又不能更新UI,只能通过这种消息机制来通知主线程更新UI,或者具体到,它就是在当前线程检索是否有消息需要传递的一个东西,一个线程只需要有一个检索的就够了。

总结下来,整个流程就是:

  1. 调用Looper.prepare(),创建和线程绑定的Looper,还有和Looper绑定的MessageQueue。
  2. 实例化Handler,获取Looper和MeesageQueue。重写其handMessage()方法。
  3. 调用Hander.sendMessage()方法,这个方法会将Message加入MessageQueue队列
  4. 然后loop()方法会将消息取出来,然后调用msg.target.dispatchMessage(msg)(Meesage.Handler.dispachMessage())最终调用我们重写的handlerMessage()方法。(loop()里面是一个死循环)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值