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

来源:互联网 发布:淘宝天猫交易额 编辑:程序博客网 时间:2024/06/07 06:00

上一篇主要说了:
(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 0