Android的消息机制深度分析

1、Looper的工作原理

Looper在Android的消息机制中扮演着消息循环的角色,Looper.loop方法是一个死循环,具体来说就是不停从MessageQueue中查看是否有新消息,如果有新消息,就会立即处理,否则就会一直阻塞在那里。如果调用Looper.quit或者Looper.quitSafely,那么Looper会调用MessageQueue的quit或者quitSafely方法来通知消息队列退出,当消息队列被标记为退出状态的时候,它的next方法就会返回为null,loop方法就会跳出死循环,loop方法跳出死循环的唯一方法就是MessageQueue的next返回为null。

Looper的构造方法:

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
在构造方法中它会创建一个MessageQueue,然后将当前线程的对象保存起来。

创建Handler对象的前提是当前线程存在Looper对象,如果没有Looper线程就会报错。那么如何为一个线程创建Looper呢?其实很简单,通过Looper.prepare()即可为当前线程创建一个Looper,通过Loopre.loop()来开启消息循环,如下所示:

        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Handler handler = new Handler();
                Looper.loop();
            }
        }).start();

Looper.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.recycleUnchecked();
        }
    }
通过不断的循环调用queue.next()方法从队列中取出msg,如果取出的为null,则return,也就是我们上面讲的调用了Looper.quit()方法。如果不为空,则执行msg.targrt.dispatchMessage(msg)方法,这个msg.targrt正是当前Looper所在线程的handler对象。

2、Handler的工作原理

Handler的作用主要包含消息的发送和接收过程。消息的发送可以动过post的一系列方法以及send的一些列方法来实现,发送一条消息的过程如下:

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    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);
    }
可以看到, Handler发送消息的过程仅仅是向消息队列中插入一条消息

再看一下Handler.post(Runnable)的执行过程:

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
可以发现post最终也是调用senMessageDelayed方法来实现的,我们来看一下getPostMessage(r)方法的代码实现:

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
首先通过Message.obtain()方法获得一个Message对象,这个obtain实际上是个单例模式的实现,然后将参数r赋值给Message的Runnable对象callback。

前面讲的Looper的loop()取得消息后最终交给Handler的dispatchMessage处理,dispatchMessage方法的实现如下:

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
当msg.callback不为空的时候,执行handleCallback(msg)方法,实现如下:

    private static void handleCallback(Message message) {
        message.callback.run();
    }
直接执行callback的run()方法。

当msg.callback为空同时mCallback为空的时候,就会执行handleMessage()方法。

3、MessageQueue的原理

MessageQueue的原理就非常简单,首先MessageQueue并不是一个真的队列,而是一个链表,链表的作用就是为了方便进行插入和删除。它主要有两个方法,一个是next()方法,负责从消息队列中取出消息,另外一个是enqueMessage()方法,主要是负责想笑兮队列中插入一条消息。


4、需要注意的问题

下面我们通过一个场景来观察一个关于执行线程的问题,代码如下:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.e("MainActivity", "11111 " + Thread.currentThread());

        final Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case 1:
                        Log.e("MainActivity", "22222 " + Thread.currentThread());
                        break;
                    default:
                        break;
                }
            }
        };

        new Thread(new Runnable() {
            @Override
            public void run() {
                Message msg = Message.obtain();
                msg.what = 1;
                handler.sendMessage(msg);
                Log.e("MainActivity", "33333 " + Thread.currentThread());
            }
        }).start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        Log.e("MainActivity", "44444 " + Thread.currentThread());
                    }
                });
            }
        }).start();
    }
对应的日志输出如下:

08-24 13:35:11.916 6250-6250/com.weilei.fragdemo E/MainActivity: 11111 Thread[main,5,main]
08-24 13:35:11.965 6250-6266/com.weilei.fragdemo E/MainActivity: 33333 Thread[Thread-175,5,main]
08-24 13:35:12.067 6250-6250/com.weilei.fragdemo E/MainActivity: 22222 Thread[main,5,main]
08-24 13:35:12.067 6250-6250/com.weilei.fragdemo E/MainActivity: 44444 Thread[main,5,main]
可以发现,handler的handleMessage()和通过handler.post的Runnable都是在主线程里面执行的。

实际上具体在哪个线程里面执行主要看当前的Looper在哪个线程,因为所有的消息最终都是通过Looper来获取,然后交给Looper所在线程的Handler对象去处理。在主线程可以直接通过Handler handler = new Handler()来构建,当然Handler也可以在子线程,通过Handler handler = new Handler(Looper.getMainLooper())创建一个主进程的Handler。

另外:view.post(runnable)实际上也是调用handler.post( )方法,在执行过程中首先构造一个handler,然后调用它的post方法。


(2)看第二个例子;

        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Handler handler = new Handler();

                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        Log.e("MainActivity", "test " + Thread.currentThread());
                    }
                });

                Looper.loop();

            }
        }).start();
输出日志如下:

08-24 16:31:05.043 9256-9256/com.weilei.fragdemo E/MainActivity: 11111 Thread[main,5,main]
08-24 16:31:05.140 9256-9282/com.weilei.fragdemo E/MainActivity: test Thread[Thread-324,5,main]
通过这个例子可以印证我们上面的总结,即Runnable具体在哪个线程执行,关键是看构建Handler的Looper对象在哪个线程。

注意:Looper.loop()方法一定要在post之后执行,否则就会一直处于阻塞状态。


(3)在看第三个例子

    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.e("MainActivity", "33333-- ");
                v.post(new Runnable() {
                    @Override
                    public void run() {
                        Log.e("MainActivity", "33333 " + Thread.currentThread());
                    }
                });
            }
        }).start();
    }
输出结果如下:

08-24 16:36:41.670 14136-14136/com.weilei.fragdemo E/MainActivity: 11111 Thread[main,5,main]
08-24 16:36:41.722 14136-14162/com.weilei.fragdemo E/MainActivity: 33333-- 
08-24 16:36:41.998 14136-14136/com.weilei.fragdemo E/MainActivity: 33333 Thread[main,5,main]
这个和我们的猜想一样,最终在主线程里面执行。另外有一点需要注意的是: view.post()一定得在onAttachToWindow()之后才能生效,这也是我们为什么要在onAttachToWindow()的super之后执行这个post方法的原因。原因我们可以看源码是怎么实现的,View的post源码如下:

    public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }
        // Assume that post will succeed later
        ViewRootImpl.getRunQueue().post(action);
        return true;
    }
当view处于attached状态的时候,attachInfo将不为空,此时直接执行handler.post(action);但是当view不处于attached状态的时候,就会执行RnQueue.post(action)方法,这个方法的作用是什么呢?还是直接看代码:

        void post(Runnable action) {
            postDelayed(action, 0);
        }
        void postDelayed(Runnable action, long delayMillis) {
            HandlerAction handlerAction = new HandlerAction();
            handlerAction.action = action;
            handlerAction.delay = delayMillis;

            synchronized (mActions) {
                mActions.add(handlerAction);
            }
        }
可以看到这个方法只是将action方法mActions中趋缓存而已,直接上并不会句执行action中的run方法,因此如果我们想保证我们的runnable能够得到执行,就得先保证View处于attached状态,分析到此结束。

可能有人在想,这里的mActions中的action会在什么时候执行了,我们在ViewRootImpl中的可以看到一个方法:

        void executeActions(Handler handler) {
            synchronized (mActions) {
                final ArrayList<HandlerAction> actions = mActions;
                final int count = actions.size();

                for (int i = 0; i < count; i++) {
                    final HandlerAction handlerAction = actions.get(i);
                    handler.postDelayed(handlerAction.action, handlerAction.delay);
                }

                actions.clear();
            }
        }
在这个中遍历所有的action,然后调用handler.post方法。同时这个方法又是在什么时候执行的呢?直击上这个方法是在ViewRootImpl的performTraversals( )中执行的,这个方法我们之前在研究View的绘制流程中见过,它正是view的绘制入口方法。


到此为止,关于Andorid的消息机制已全部分析完毕,任何问题都可以从源码中找到答案。另外补充一下,一般刷新UI除了用handler的post活着sendMessage外,还可以调用Activity中的runOnUiThread()方法和view.post(runnable)方法,其实他们的原理都是相同的。


猜你喜欢

转载自blog.csdn.net/wei_lei/article/details/77527730