深入了解Handler消息机制(二)

上一篇主要说了:
(1)Message,MessageQueue,Looper,Handler的工作原理就像是工厂的生产线。待加工的产品就是Message,“传送带”就是MessageQueue,电动机就是Looper,工人们就对应于处理事件的Handler。
(2)Android系统用链表来实现Message的缓存消息池。

我们知道Android的应用程序的入口是ActivityThread.main()方法。在该方法中首先会创建Application和默认启动的Activity,并且将他们关联在一起。而该应用的UI线程的消息循环也是在这个方法中创建的,具体代码如下:

public static void main(String[] args) {
        //代码省略
        Process.setArgV0("<pre-initialized>");
        //1,创建消息循环Looper,就是UI线程的Looper
        Looper.prepareMainLooper();
        // 2,启动ActivityThread,这里最终会启动应用程序
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        // End of event ActivityThreadMain.
       Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
       // 3,执行消息循环
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
}

执行了ActivityThread.main方法后,应用程序就启动了,UI线程的消息循环也在Looper.loop函数中启动了。此后Looper就一直从消息队列中取消息,然后Handler处理消息。用户或者系统通过调用Handler不断地往消息队列中添加消息,这些消息不断地被取出、处理、回收,使得应用程序迅速地运转起来。
例如:我们在子线程中执行完耗时操作后通常需要更新UI,但我们都知道不能再子线程中更新UI。此时最常用的手段就是通过Handler将一个消息post到UI线程中,然后再在Handler的handleMessage方法中进行处理。
但是有一点要注意,就是如果用在不传递UI线程的Looper的情况下,那么该Handler必须在主线程中创建。

为什么必须这么做呢?

其实每个Handler都会关联一个消息队列,消息队列被封装在Looper中,而每个Looper又是ThreadLocal的,也就是说每个消息队列只会属于一个进程。因此如果一个Looper在进程A中创建,那么该Looper只能被该线程访问。而Handler则是一个消息投递,处理器,它将消息投递给消息队列,然后这些消息在消息队列中被取出,并且执行在关联在消息队列的线程中。默认情况下,UI线程也就是调用了Looper.getMainLooper方法,创建Looper之后最后会执行Looper.loop方法开启消息循环。

那么Handler是如何关联消息队列和线程的呢??

我们先看一下Hnadler的构造函数:

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));
    }
public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
 public Handler(Callback callback, boolean async) {
         //代码省略
        mLooper = Looper.myLooper();//获取Looper
        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;
    }

我们看到myLooper方法是通过sThreadLocal.get()来获得的。那么Looper对象又是在什么时候存进去的呢?在上面的源代码中我们发现prepare方法中创建了一个Looper对象并且将该对象设置给了sThreadLocal,这样队列就与线程关联上了。
我们再回到Handler中来,Looper属于某个线程,消息队列存储在Looper中,因此,消息队列就通过Looper与特定的线程关联上。而Handler又与Looper、消息队列关联,因此,Handler最终就和线程、线程的消息队列关联上了,通过该Handler发送的消息最终就会被执行在这个线程上。这就能解释上面提到的问题了,“在不传递Looper参数给Handler构造函数的情况下,用更新UI的Handler“必须在主线程中创建?”就是因为Handler要与主线程的消息队列关联起来,这样handlerMessage才会执行在UI线程中,更新UI才是被允许的。
创建了Looper之后,会调用Looepr的loop函数不断地从消息队列中取出消息。具体代码如下:

  public static void loop() {
        final Looper me = myLooper();//获取Looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //通过Looper对象拿到消息队列
        final MessageQueue queue = me.mQueue;
        // 代码省略
        for (;;) {//死循环,即消息循环
            Message msg = queue.next();  
            if (msg == null) { 
                return;
            } 
            //消息分发处理
            msg.target.dispatchMessage(msg);
     //代码省略
            //回收消息
            msg.recycleUnchecked();
        }
    }

从上述代码可以看出,loop方法其实就是建立一个死循环,容纳后通过从消息队列中逐个取出消息,最后就是处理消息,回收消息的过程。代码中调用的是MessageQueue的next函数获取下一条要处理的消息。这个MessageQueue在Looper的构造函数中构建。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值