Android消息机制梳理

从源码的角度解析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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值