Android 消息机制

一. 导论

  在学习Android基础知识的时候,我们会接触到一个基本的概念,那就是消息机制,之后便会接触到Handler,Looper,MessageQueue,Message之间的关系,接下来我们来解析这个流程

二. Handler的说明

  Handler主要是接收子线程发送的数据,并且用此数据配合主线程更新UI,用来与主线程交互。如果此时需要一个耗时的操作,例如:联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象,如果5秒钟还没有完成的话,会收到Android系统的一个错误提示”强制关闭”. 这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,但是当子线程中有涉及到操作UI的操作时,就会对主线程产生危险,也就是说,更新UI只能在主线程中更新,在子线程中操作是危险的. 这个时候,Handler就出现了来解决这个复杂的问题,由于Handler运行在主线程中(UI线程中),它与子线程可以通过Message对象来传递数据,这个时候,Handler就承担着接受子线程传过来的(子线程用sendMessage()方法传递)Message对象(里面包含数据), 把这些消息放入主线程队列中,配合主线程进行更新UI。
  Handler可以分发Message对象和Runnable对象, 每个Handler实例,都会绑定到创建它的线程中(一般是位于主线程), 也就是说Handler对象初始化后,就默认与对它初始化的进程的消息队列绑定,因此可以利用Handler所包含的消息队列,制定一些操作的顺序

三. Handler的工作原理
3.1 MessageQueue

  这是一种数据结构,表示一个消息队列,存放消息的地方。每一个线程最多只可以拥有一个MessageQueue。通常使用一个Looper对象对该对象的MessageQueue进行管理。
  主线程创建的时候,系统会创建一个默认的Looper对象,而Looper对象的创建,会自动创建一个MessageQueue,但是当创建其它线程的时候,系统并不会主动为其创建Looper对象,当我们需要时,通过调用prepare()方法来实现。

3.2 Message

  消息对象,MessageQueue中存放的对象。一个MessageQueue包含多个Message对象。Message实例对象的获得,通常使用Message.obtain()方法或者Handler.obtainMesssage()方法来获取。
  对于上面的Message.obtain()方法,都有多种重载的方法可供选择。它的创建并不一定是直接创建Message实例,而是先从Message Pool(消息池)中看有没有可用的Message实例,存在则直接取出并返回这个实例;如果Message Pool里面没有可用的Message实例,则用给定的参数创建一个Message对象。调用removeMessages()时,将Message从MessageQueue删除,同时放入到Message Pool中。
  当Message和messageQueue的定义清楚之后,如何管理MessageQueue便是一个问题,在Android中Looper的出现就是为了帮助我们管理MessageQueue。

3.3 Looper

  这是MessageQueue的管理者。每一个MessageQueue都不能脱离Looper而存在,Looper对象的创建是通过perpare()方法来实现的。同时每一个Looper对象和一个线程关联。通过调用Looper.myLooper()可以获得当前线程的Looper对象,创建一个Looper对象时,会同时创建一个MessageQueue对象。除了主线程有默认的Looper对象,其它线程是没有的,所以不能接受Message。如需接受,需要自己自定义一个Looper对象(通过Prepare()方法),这样线程就有了自己的Looper对象和MessageQueue数据结构。
  Looper从MessageQueue中取出Message,然后交由Handler的handleMessage进行处理。处理完成之后,调用Message.recycle()方法将其放入Message Pool中。

3.4 Handler

  消息的处理者,负责发送消息和处理消息。Handler负责把将要传递的消息封装成Message,通过调用Handler对象obtainMessage()方法来实现;将消息传递给Looper,这是通过Handler对象的sendMessage()来实现的;继而由Handler将Message放入MessageQueue中,当Looper对象看到MessageQueue中含有Message,就通知Handler进行处理,而handler对象接收到该消息时,调用该方法的handleMessage()来处理。

四. 源码分析

4.1 我们首先来看一下Looper的构造方法

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    以上的方法中创建了一个新的MessageQueue,这也就解释了上面的创建Looper的同时会创建一个MessageQueue

4.2 之后便是分析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));
    }
    sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。可以看到,在第5行,将一个Looper的实例放入了ThreadLocal,并且2-4行判断了sThreadLocal是否为null,否则抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例。

对于以上的代码,我们提出了一个问题:
1. 对于一个线程,如果拥有多个Handler,此时这个线程有几个消息队列和几个Looper?

只有一个消息队列,一个Looper,在Looper代码里面可以看到,在prepare方法里面可知,Android规定一个线程只能有一个与自己关联的Looper,在Looper的构造方法定义了一个MessageQueue,故就只有一个Looper和一个MessageQueue

4.3 以下是loop方法

 public static void loop() {
        // sThreadLocal存储的Looper实例,就是prepare方法里面存储的
        final Looper me = myLooper();
        // 如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        // 拿到了looper实例中的mQueue(消息队列)
        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 (;;) {
            // 取出一条消息。如果存在消息但是时间未到,就会发生阻塞,
            // next()方法是一个无线循环的方法,如果消息队列中没有消息,那么next()方法会一直阻塞,当有新消息到来时,next会将这条消息返回同时也将这条消息从链表中删除。
            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
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            // 将消息交给msg的target的dispatchMessage()方法去处理。msg的target对象是什么?其实就是handler对象,之后我们会分析这个值是在什么地方进行设置的
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

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

先看第一句

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
方法直接返回了sThreadLocal存储的Looper实例,如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。
其余的在代码里面进行注释

对于以上的代码,我们提出了一个问题:
msg.target 是在什么地方赋值的?

这个是在Handler里面进行设置的,我们接下来分析这个赋值的过程

4.4 接下来分析Handler方法

    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }
       // 通过Looper.myLooper()获取了当前线程保存的Looper实例
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        // 获取了这个Looper实例中保存的MessageQueue(消息队列),因为一个looper只有一个MessageQueue,也就是得到了与当前线程绑定MessageQueue
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

4.5 现在分析常见的SendMessage(msg)方法

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

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        // 看到没,这里就是将当前的Handler赋值为msg.target
        **msg.target = this;**
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

  由上述的代码可知,msg.target是在handler发送消息的时候赋值的,明确该消息的处理的对象,并且之后将该Message插入消息队列,接下来分析消息队列的插入

4.6 queue.enqueueMessage(msg, uptimeMillis) 消息分析

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            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;
                // 插入新的消息,插入的顺序是通过msg.when 来确定插入的位置的
                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;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

  由上面可以看出消息的插入是通过链表来实现的,插入的位置是通过msg.when 来确定位置的。

  分析到这,是不是还有什么东西没有分析,对了,你一定想到了,在定义Handler的时候,你要复写handleMessage 方法,但是此方法是在什么地方调用?
我们在looper的时候,通过msg.target.dispatchMessage(msg);这句话发送消息,我们可以看一下这个方法,由于msg.target 是handler,所以在Handler里面去寻找这个方法:

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // 看到没,就是在此处进行的调用,
            handleMessage(msg);
        }
    }

至此,消息机制基本分析完毕,我们还有一个需要注意的
我们应该注意到,上面Message里面还有一个Callback的Runnable对象,由此引发了下面的内容

   handler 不仅可以发送msg,还可以传递Runnable对象。用于通过Handler绑定的消息队列,安排不同操作的执行顺序。
以下的部分说明了Handler的post方法的线程和UI线程有什么关系。
这里需要说明,有时候为了方便,我们会直接写如下代码:

mHandler.post(new Runnable() {  
         @Override  
         public void run() {  
             Log.e("TAG", Thread.currentThread().getName());  
             mTxt.setText("yoxi");  
         }  
});

  然后run方法中可以写更新UI的代码,其实这个Runnable并没有创建什么线程,而是发送了一条消息,下面看源码:

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

  可以看到,在getPostMessage中,得到了一个Message对象,然后将我们创建的Runable对象作为callback属性,赋值给了此message.

注:产生一个Message对象,可以new ,也可以使用Message.obtain()方法;两者都可以,但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存。

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.sendMessage一样,调用了sendMessageAtTime,然后调用了enqueueMessage方法,给msg.target赋值为handler,最终加入MessagQueue.
可以看到,这里msg的callback和target都有值,那么会执行哪个呢?
其实上面已经贴过代码,就是dispatchMessage方法:

public void dispatchMessage(Message msg) {  
       // 如果不为null,则执行callback回调,也就是我们的Runnable对象。
       if (msg.callback != null) {  
           handleCallback(msg);  
       } else {  
           // 这儿需要继承Handler.Callback接口
           if (mCallback != null) {  
               if (mCallback.handleMessage(msg)) {  
                   return;  
               }  
           } 
           // 这儿就是我们要找的调用处
           handleMessage(msg);  
       }  
   } 
三次匹配只是形式上的不同,具体的使用那一种的方式,需要我们自己去取舍 

>

五. 总结

  1. 当我们调用handler.sendMessage(msg)方法发送一个Message时,实际上是将该消息发送到与当前线程绑定的MessageQueue里面,之后与当前线程绑定的looper会不断的从MessageQueue里面取出Message,然后调用msg.target.dispatchMessage(msg)方法,到handler的handleMessage(msg)里面去处理该消息。
  2. Handler不仅可以发送消息,还可以发送Runnable对象,但是要注意此时并没有新创建一个线程。
  3. 对于产生一个Message对象,可以new ,也可以使用Message.obtain()方法;两者都可以,但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存。
  4. 一个Thread可以对应多个Handler,一个Thread对应一个Looper和一个MessageQueue,也就是在同一个线程内,Handler共享同一个Looper和MessageQueue
  5. 主线程的Looper和MessageQueue是在ActivityThread的main()方法里面初始化的,所以我们在主线程编写代码的时候,不用写这些代码。

参考文章
  1. http://blog.sina.com.cn/s/blog_77c6324101016jp8.html android中handler用法总结
  2. http://tanghaibo001.blog.163.com/blog/static/9068612020111287218197/ Android 中Message,MessageQueue,Looper,Handler详解+实例
    但是该网站的文章不好查看,看一篇转载的位置,比较适合观看 该位置为:http://www.open-open.com/lib/view/open1331276072249.html
  3. http://blog.csdn.net/yuzhiboyi/article/details/7562245 Android之Handler详解(一)
  4. http://www.cnblogs.com/hnrainll/p/3597246.html Android HandlerThread 的使用及其Demo
  5. http://blog.csdn.net/lmj623565791/article/details/38377229 Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
  6. http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html android的消息处理机制(图+源码分析)—Looper,Handler,Message
  7. 最后这一篇是讲解具体的Java和native的部分
    http://www.cnblogs.com/angeldevil/p/3340644.html Android消息处理机制(Handler、Looper、MessageQueue与Message)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值