Anroid消息机制原理分析

版权声明:转载需申明作者与链接 https://blog.csdn.net/DYQ_Lin/article/details/82890621
前言

最近通过微信公众号推文以及博客文章学习了Android的消息机制的原理,然后抽空写下了这篇文章,对自己学到知识进行梳理,也方便以后自己查阅。
因为Android的UI是线程不安全的,而我们处理完成异步任务的时候,常常都需要更新我们的UI界面,这样,就产生了一个矛盾:也就是我的子线程需要更新UI,而这个操作又必须在主线程中完成。所以,就有了我们接下来要分析的消息机制。

消息机制的大概流程:

启动系统后,会在主线程创建一个Looper对象,然后通过这个Looper对象开启一个死循环,循环会不断地从消息队列中(MessageQueue)中取出待处理的消息(Message),并回调Handler中的方法处理消息。消息队列中的消息是从哪里来的呢?没错,就是通过Handler发送过去的。流程也可用下面的图表示出来:

在这里插入图片描述

消息机制源码分析:

我们在使用到消息机制进行更新UI的时候的操作步骤是怎么样的呢?

  1. 在主线程中创建一个Handler对象,传入一个Handler.Callback接口的匿名类对象,并重写该对象的handlerMessage()方法。
  2. 在子线程中创建一个Message对象,把要发送的消息放在Message对象中,然后调用Handler的sendMessage()方法把Message插入到MessageQueue中
  3. 最后主线程中重写的handlerMessage()方法会被调用,更新UI的操作就完成啦

我们就跟着上面的步骤去看一下消息机制里面都做了些什么吧。没错,我们就要去看看源码了,其实看源码不难,跟着思路来走就不会乱了。首先进入Handler的构造方法:

   //我们传入Handler.Callback对象调用的构造方法 
    public Handler(Callback callback) {
        this(callback, false);
    }

    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对象
       //如果Looper对象为空,就直接抛出异常,提示用户没有使用Looper.prepare()创建Looper对象
       //我们还没创建过Looper对象,会不会报错呢?不急,我们接着往下看 ,肯定与Looper.prepare()有关
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //获取MessageQueue消息队列
        //其实MessageQueue里面的数据结构是一个单链表
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

好啦,这样,我们的一个Handler对象就创建好了(Looper.prepare()这个方法我们后面会讲到,先顺着思路走下去),并且我们可以看到,Handler中持有Looper和MessageQueue的引用。接下来我们就要用到这个Handler对象往消息队列中插入消息了,让我们看看Handler.sendMessage(),看它是如何往消息队列中插入消息的

   //我们使用时调用的方法 
    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) {
    	//target其实就是Handler对象,通过msg.target=this实现Message与Handler的绑定
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //最后,调用了MessageQueue的enqueueMessage方法
        return queue.enqueueMessage(msg, uptimeMillis);
    }

	//下面的源码中省略了一些无关、影响阅读的源码,想看完整源码的可以自行去AS中查看
    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        synchronized (this) {
            Message p = mMessages;
            if (p == null ) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
            } else {
                Message prev;
                //前面说过,MessageQueue数据结构是单链表,下面就是遍历单链表,找到链表尾部
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;//把消息插入到尾部
            }
        }
        return true;
    }

这时候,我们就已经成功把消息(Message)插入到MessageQueue中了,并且我们知道了Handler把自己封装进了Message当中。走完这两步,我们好像走进了一个死胡同,走不下去了?先来看看下面几个问题:

  1. Looper对象是在哪里被创建的?
  2. Handler中的handlerMessage()方法是怎么被回调的?

我们在上面的步骤中,没有创建过Looper对象,而Looper对象却已经存在了,那唯一的一种可能就是,当我们启动系统的时候,系统已经帮我们创建好Looper对象了。我们在ActivityThread类中找到main()方法,ActivityThread类是一个隐藏类,我们可以在SDK文件夹中查找出来:

public static void main(String[] args) {
        ......
        Looper.prepareMainLooper(); //创建一个Looper对象

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop(); //开启循环

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

果然,我们能在main()发现Looper。main()方法中通过Looper.prepareMainLooper()创建一个Looper对象,prepareMainLooper()方法实际上就是调用Looper.prepare(),没错,就是我们之前发现的方法,接下来让我们看看这个方法做了什么吧

	public static void prepareMainLooper() {
        prepare(false);
        ......
    }
    
	......
	
	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()中就是new了一个Looper,并把Looper set进ThreadLocal中(ThreadLocal是什么来的?我暂时还没有去了解,有时间会补充),所以Looper在系统启动时就创建好了,我们创建Handler时才不会报错
Looper创建完成之后,然后Looper.loop()就会开启一个循环,开启循环做什么呢?就是之前说过的,从MessageQueue中不断的取出消息来

   //剔除了一些影响阅读的代码后,可以看到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;
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            try {
                msg.target.dispatchMessage(msg);
            } finally {
               ......
            }
        }
    }

很明显可以看出loop方法一直遍历MessageQueue,阻塞线程,直到获取到一个Message,然后调用Message的一个成员变量target的dispatchMessage方法。之前说过了,target其实就是Handler,dispatchMessage方法最终就调用我们重写的Handler的handlerMessage方法

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

以上就是我们在主线程中消息机制的原理,让我们整理一下上面的知识:

  • Handler:在消息机制中,是作为消息的发送方和处理方。消息在一个线程中通过Handler发送到MessageQueue中。
  • Looper:在消息机制中,是作为消息队列的管家。不停的从消息队列中获取消息,获取到消息后,根据Message中绑定的Handler对象调用Handler中的dispatchMessage方法,进而调用到开发者重写的handlerMessage方法进行消息的处理
  • MessageQueue和Message:MessageQueue就是存放Message的地方,实质上是一个单链表的结构,有新的Message来的时候,就把它放在表尾,当Looper来取消息时就把表头的Message给它。Looper拿到消息后,可以从Message中拿到消息的发送方。

知道消息机制的原理后,我们就可以轻松的在任何线程下使用handler了:

    new Thread() {
         @Override
         public void run() {
             Looper.prepare();
             Looper.loop();
             Handler handler = new Handler(new Handler.Callback() {
                 @Override
                 public boolean handleMessage(Message msg) {
                     return false;
                 }
             })
         }
     }.start();
  1. 首先用Looper.prepare()创建一个Looper并初始化Looper持有的MessageQueue
  2. 然后用Looper.loop()方法开启循环,从MessageQueue中取消息,并调用handler的dispatchMessage方法处理消息,如果消息队列里面没有消息,循环就会阻塞进入休眠状态,等有消息时就会被唤醒
  3. 最后再new一个Handler,Handler构造方法会获取到Looper和Looper的MessageQueue对象,然后通过sendMessage方法往消息队列中插入消息。

最后,我们对消息机制有一些了解了,先写这么多吧,如有错误欢迎指出。

猜你喜欢

转载自blog.csdn.net/DYQ_Lin/article/details/82890621