Android消息机制梳理

来源:互联网 发布:了不起的nodd.js 编辑:程序博客网 时间:2024/06/06 01:01

从源码的角度解析android的消息机制,结合7.0源码,重新梳理一下android的消息机制。 Message ,Looper,Handler,MessageQueue的关系。

我们都知道Android的UI不是线程安全的,所以不能在子线程中操作UI组件,如果子线程中有操作UI的需求。我们都会通过handler来进行线程间的通信。

例如:

    Handler handler=new Handler(){        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            //操作ui        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        new Thread(new Runnable() {            @Override            public void run() {                //构建Message对象                Message message=Message.obtain();                //发送消息                handler.sendMessage(message);            }        }).start();    }

例如上面的代码,在子线程中,构建一个message对象,然后通过handler发送这个message对象。然后我们就可以在handler的handleMessage方法中接收这个消息,然后操作ui组件。

这个代码我们写的太多了,闭着眼睛也可以写了。但是如果要深究原理的话,就会有很多疑问。
比如:
1.为什么handleMessage里方法就可以操作ui组件呢(ps:在android中,我们把响应用户操作的线程称为主线程,也叫ui线程。线程名为:”main”,既然handleMessage方法中能够操作ui组件,那说明他执行的线程就是在main线程中的,但为什么它执行在main线程中的呢)?
2.为什么handler.sendMessage方法后,为什么会回调handleMessage呢?他们之间的调用栈是怎样的?

第一个问题,有点大,我们先来看第二个问题! 跟进sendMessage的源码,发现除了postAtFrontOfQueue这个方法外,所有的handler的sendxx,postxx方法,最后都会回调sendMessageAtTime方法。代码如下:

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

那个方法没有调用sendMessageAtTime方法,但是在里面调用了sendMessageAtFrontOfQueue方法。这个方法的实现和sendMessageAtTime完全相同。然后我们看sendMessageAtTime这个方法的实现,可以看到先给MessageQueue 对象赋值,然后调用了enqueueMessage方法,代码如下。 最后调用了queue对象的enqueueMessage方法。

   private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

我们先整理一下MessageQueue对象的值是从哪来来的,然后再看enqueueMessage方法的实现。

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

可以看到我们mQueue是从mLooper中取出来的。 那这个Looper又是干什么的呢? 带着疑问,我继续看一下MessageQueue的enqueueMessage方法。

         ......                       msg.markInUse();           msg.when = when;           Message p = mMessages;           boolean needWake;            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;            }        ......    }

enqueueMessage的方法代码量有点多,具体的逻辑看不太懂。 但是大概的意思是判断了一通,然后把message对象插入到链表中。代码分析到这里就有点蒙了 。 what… 我们从handler.sendxx方法,沿着调用栈,分析了一通,发现handler.sendxx方法就是把message插入到Looper对象的MessageQueue链表中。 那handleMessage方法啥时候调用呢? 不说清楚别想走 .哈哈。 别急,这个时候我们就需要回头想想,平时我们在主线程给子线程中发消息,需要构建子线程handler对象,然后还需要做什么呢? 对了。我们需要构建Looper然后,调用Looper的Loop方法。 例如下面这样:

public class MainActivity extends Activity {    Handler handler;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //向子线程发送        Message message=Message.obtain();        handler.sendMessage(message);        new Thread(new Runnable() {            @Override            public void run() {                Looper.prepare();                handler=new Handler(){                    @Override                    public void handleMessage(Message msg) {                        super.handleMessage(msg);                        //执行在子线程                    }                };                Looper.loop();            }        }).start();    }}

我们想想,同样是handler发送消息,为什么子线程中需要调用Looper.prepare(), Looper.loop()呢,而主线不需要,还有就是发送消息和接收消息,为什么要调用Looper.prepare()和 Looper.loop()方法呢? 想清楚这两个问题,我们之前的疑惑就自然而然的解开了。
我们先看一下Looper.prepare()方法:

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

可以看到在prepare方法中,构建了一个Looper对象,然后放入到ThreadLocal对象中。看到这里我不禁想起之前看到的handle构造方法里面的那段代码:

 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;

原来这个looper对象是用来构建handler的时候使用的呀。然后跟进去myLooper方法,

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

可以看到Looper对象是从ThreadLocal中取出的。 这里得说明一下ThreadLocal类的作用,这个类日常开发的时候很少使用。但是在一些场景用起来却是很适合。这个类可以保证每个线程都保存一份变量,线程与线程间的数据相互隔离。比如,我们在哪个线程中get出来的对象,就是我们之前在这个进程set进去的对象。

然后看一下Looper.myLooper()方法,

        final Looper me = myLooper();        final MessageQueue queue = me.mQueue;        ......        for (;;) {            Message msg = queue.next(); // might block        ......            try {                msg.target.dispatchMessage(msg);            } finally {                if (traceTag != 0) {                    Trace.traceEnd(traceTag);                }            }         ......        }    }

Looper.myLooper()方法中代码比较多,我们只看关键代码,可以看到先是取出当前线程的looper对象,然后用一个无限循环从Looper中的queue中读取消息(queue.next()),然后传递给msg.target的dispatchMessage方法。这里msg的tag就是我们的handler对象。 分析到这里,我们就梳理清楚handler消息机制了,我们总结一下:先是handler发送消息,插入到当前线程Looper中MessageQueue链表中,然后在Looper的loop方法中无限循环从MessageQueue读取消息,传递给handler.dispatchMessage方法! 然后我们看一下dispatchMessage方法,代码如下:

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

可以看到我们处理消息有3个顺序,首先是msg.callback,mCallback.handleMessage,handleMessage,跟上源码看一下赋值过程:首先是msg.callback的赋值过程,代码如下:

  public final boolean post(Runnable r)   {       return  sendMessageDelayed(getPostMessage(r), 0);  }
  private static Message getPostMessage(Runnable r) {        Message m = Message.obtain();        m.callback = r;        return m;    }

这个callback就是我们在handler.post的时候赋值的runable对象。然后回到dispatchMessage方法,如果
msg.callback不为空执行就执行handleCallback,代码如下:

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

所以我们知道handler的post方法也是执行在主线程中的。而且post处理消息的优秀级最高。

然后我们看一下mCallback,这个mCallback就是我们下面这种方式创建的handler的时候的Handler.Callback对象:

  Handler handler=new Handler(new Handler.Callback() {        @Override        public boolean handleMessage(Message msg) {            return false;        }    });

Handler.Callback处理消息的优先级也比handler.post低,但是高于handleMessage方法。

现在我们分析清楚了handler的消息机制,以及message是怎么传递和被处理的。然后我们回头看看主线程中的消息是怎么被处理的,它也应该和子线程一样使用handler一样,需要构建一个looper,然后调用looper的loop方法。代码在哪里呢,我们找一下:

    public static void main(String[] args) {        ......        Looper.prepareMainLooper();        ......        Looper.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }

原来ActivityThread的main方法中。我们知道,当我们应用程序启动的时候,首先Zygote fork出一个新的进程,然后会调用ActivityThread的main方法。 原来我们在主线程中使用handler的时候系统已经默认帮我们做了Looper的初始化工作。

分析到这里我们已经知道,handler发送消息之后是怎么传递的,会回调handler的哪个方法,子线程和主线程的looper初始化工作是怎么实现的。 但是还是有一个最大的疑问,为什么消息传递到handler的dispatchMessage方法中后,我们就发现如果是主线程的handler,dispatchMessage方法就执行在主线程,如果是子线程的handler,dispatchMessage就执行在子线程? 换句话说为什么是 dispatchMessage方法是执行在Looper所在的线程? >- -< 回答这个问题,因为dispatchMessage方法是在Looper.loop方法中回调的,所以当前执行在loop方法所在的线程。loop方法执行在Looper所在的线程。

然后我们先整理一下其他的几个特殊的方法:
Activity的runOnUiThread方法

    public final void runOnUiThread(Runnable action) {        if (Thread.currentThread() != mUiThread) {            mHandler.post(action);        } else {            action.run();        }    }

可以看到如果不是在主线程,就调用handler的post方法(这里的这个mHandler对象是Activity的,构造在主线程中),如果是在主线程就直接执行。

View的post方法

    public boolean post(Runnable action) {        final AttachInfo attachInfo = mAttachInfo;        if (attachInfo != null) {            return attachInfo.mHandler.post(action);        }        // Postpone the runnable until we know on which thread it needs to run.        // Assume that the runnable will be successfully placed after attach.        getRunQueue().post(action);        return true;    }

查看源码可知,这里的attachInfo!=null表示View已经添加到Window中,就执行主线程的handler.post方法(attachInfo.handler是在Activity的handler对象)。

如果View没有attach到window中,那就会在ViewRootImpl下一次performTraversals中执行。 这种情况比较复杂,可以看一下文章后面的参考链接中讲解View.post的文章。

总结几点:

  • 1.handler的.dispatchMessage方法是执行在Looper所在的线程的。
  • 2.每个线程的Looper只有一个,也就是说每个线程的MessageQueue只有一个,无论这个线程中有多少个handler。 ps:现在很多的性能监控组件会监控app的Ui卡顿情况,很多的实现思路就是,定时的向主线程的Looper发送消息,判断消息处理的时间间隔是否达到一定的时间。 从而判断ui线程是否卡顿。

参考链接:
http://blog.csdn.net/a740169405/article/details/69668957

阅读全文
1 0