美文网首页Android Framework源码分析Android开发Android开发
关于Android消息机制你所需要知道的细节

关于Android消息机制你所需要知道的细节

作者: lowett | 来源:发表于2016-08-06 09:53 被阅读106次

    Android的消息机制几乎是面试必问的话题,当然也并不是因为面试,而去学习,更重要的是它在Android的开发中是必不可少的,占着举足轻重的地位,所以弄懂它是很有必要的。下面就来说说最基本的东西。

    Looper

    作用:

    • 关联起Thread
    • 循环取出消息

    1、Looper是否可以直接实例化?

    Looper构造方法是私有的,其中做了两件事

    • 创建一个MessageQueue
    • 得到与之对应的Thread
     private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    

    2、一个线程能对应多个Lopper?

    不能,一个线程对应一个Looper对象,通过ThreadLocal保证一个线程只有一个Looper与之对应,如果多次调用Looper.prepare();则会抛出运行时异常。

     private static void prepare(boolean quitAllowed) {
       if (sThreadLocal.get() != null) { // 查看是否有looper与当前线程对应
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
    

    3、Looper是无限循环,会阻塞吗?

    是,当开启一个loop后是一个死循环,从MessageQueue中取出消息,处理消息,但是也有可能退出,在没有消息后退出循环。

        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;
        // 略
          for (;;) {
              Message msg = queue.next(); // might block
              if (msg == null) { // 当没有消息的时候,退出
                  // No message indicates that the message queue is quitting.
                  return;
              }
            // 略
              msg.target.dispatchMessage(msg);
         }
    

    4、可以再次调用Looper.prepareMainLooper吗?

    不可以,Looper.prepareMainLooper最终也是调用prepare(),同2.

    public static void prepareMainLooper() {
        prepare(false); // 创建一个Looper
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been               prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    

    5、MainLooper什么时候创建的?

    MainLooper是启动Activity创建ActivityThread(并不是一个Thread)时候创建,所以不能多次创建。

    public static void main(String[] args) {
      // 略
       Process.setArgV0("<pre-initialized>");
      Looper.prepareMainLooper();
      // 略
      ActivityThread thread = new ActivityThread();
      thread.attach(false);
      // 略
      if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
      }
    
      // 略
      Looper.loop();
      throw new RuntimeException("Main thread loop unexpectedly exited");
      }
    }
    

    Handler

    作用:

    • 发送消息到MessageQueue
    • 处理消息

    1、Handler如何与Looper、MessageQueue关联起来?

    我们知道一个Looper对应一个Thread,一个Looper包含一个MessageQueue。当我们创建Handler时就会从当前线程中取出与之对应的Looper,让后在从Looper中取出MessageQueue。

    // 1、自动获取
    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; // 取出MessageQueue
        mCallback = callback;
        mAsynchronous = async;
    }
    // 2、传递一个Looper进来
    public Handler(Looper looper, Callback callback, boolean async) {
            mLooper = looper;
            mQueue = looper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

    Message

    单项链表结构。

    作用:

    • 数据的载体

    1、消息如何复用的?

    从全局消息池(链表结构)中

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
    

    2、Message为什么能传递?

    Android中想要传递对象要么实现Serializable要么Parcelable,在这里是实现了Parcelable接口。

    public final class Message implements Parcelable {
      // 略
    }
    

    3、如何与Handler关联?

    我们知道在消息传机制中Handler充当着“快递员”的角色,那么他又是如何与“货物”--Message发生关系呢?实际上Message有一个成员变量target他的类型正是Handler,

    /*package*/ Runnable callback;
    
    public int arg1; 
    
    public int arg2;
    
    public Object obj;
    
    /*package*/ Handler target; // 关键点
    

    当我们通过Handler去send一个Message时候最终都会为target赋值为this,即当前的Handler。

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

    另为如果是通过Message.Obtain(),获取的复用Message也会为其赋值。

    多说一句,Handler.obtainMessage()调用的就是Message.Obtain()。

    public final Message obtainMessage(){
        return Message.obtain(this);
    }
    

    总结:

    通过一系列的包涵关系,最终Looper、Handler、Message、MessageQueue即发生关联,从而形成一个闭合,开启消息循环。

    困惑

    最近一直在看这方面的知识,但是能力有限,还是有不少困惑,如果有错误,或你理解下面的问题请联系我fvaryu@qq.com,愿与君交流学习,谢谢

    1、Message中的sPool,哪里初始化的?为什么Message.obtain()中不会抛异常?

    2、ActivityThread并不是线程,为什么可以创建一个Looper,Main Thread什么时候创建?

    3、为什么序列化了的对象就可以传递?与Binder有关?

    4、MessageQueue对应的是NativeMessageQueue,具体实现需要学习?

    5、Loop.loop(),会退出吗?退出时机是什么?如果会退出,那么主线程同样会退出吗?
    原文链接

    相关文章

      网友评论

      • z彭:Loop.loop() 应该会退出的,在loop()死循环里面有个判断for (;;) {if (msg == null) {return;} },
        // No message indicates that the message queue is quitting. 当队列里,没有消息,会退出循环,但退出之后,会发生什么,那就还是不太懂了。
      • z彭:感谢楼主,看了之后,感觉把之前看过的文章都可以连起来了。

      本文标题:关于Android消息机制你所需要知道的细节

      本文链接:https://www.haomeiwen.com/subject/masksttx.html