Android源码分析之消息机制


说明:本文是基于Android6.0源码来分析的

  • 这片文章主要是从源码的角度来分析Android中的消息机制是如何运行的,并不会介绍如何使用Handler。
  • Android的消息机制可以说是Android的血液,流淌在不同的app之间,催动这各种事件有序的执行。
  • Android进程在启动的时候会调用ThreadActivity的main方法,从main方法中我们可以看出,Android的app进程启动以后只有一个线程ActivityThread他是main线程,除了创建主线程方法以外,还为我们的主线程准备了mianLooper,最后开启了一个Looper 循环

整个消息机制的一个重要用途,就是线程间通信,而且大部分都是工作线程向主线程发送数据和消息。那么为什么Android系统要这样设计呢?其实典型的Android应用,都是事件驱动的GUI程序,跟Window的GUI程序很类似。这种程序,特点就是基于事件运行,比如点击事件或者滑动事件。所以这种情况下,肯定是要有一个专门的线程来负责事件的监听和分发。在Android中,系统默认启动的主线程,就干这个事情了。
由于该消息分发线程有着自己独特的任务,所以如果该线程阻塞了的话,系统就会出现无响应的情况。这样,自然就不可能把耗时的任务放在该线程中,所以官方推荐是把耗时的任务放到工作线程中执行。但是很多时候,耗时任务的执行结果,都是要反馈到UI上的。而Android中的View,是不能在非UI线程中更新的,因为View不是线程安全的,没有同步,所以必须要把数据通过线程间通信的模式,发送到UI线程,这能才可以正常更新UI。

所以大家会问,Android为什么不把View设计成线程安全的呢?那么在Java这种指令式编程语言中,线程安全就是意味着要加锁。其实我们可以思考一下,整个View响应事件,事件从屏幕产生,经过Framework,最后到kernel,然后kernel处理完成后,向上传递到framework,然后又传递到View层。如下图所示:
那么如果整个流程都是线程安全的话,就会面临着两个相对的加锁流程,这种反方向的加锁,很容易就会导致死锁的情况发生。这是绝大多数GUI系统存在的问题,所以绝大多数GUI系统都是采用事件分发机制来实现的。所以说Android为什么设计成单线程模型了。

参考博客: Android消息机制探索(Handler,Looper,Message,MessageQueue)


public static void main(String[] args) {
    ...
    
    //为主线程准备一个loop
        Looper.prepareMainLooper();

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

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

        ...
        
        //开启消息循环。
        Looper.loop();
    
    
    ...
}

prepareMainLooper方法主要是准备Lopper,里面还掉用了 prepare(false);方法,注意一下参数,传的是false,也就是不允许主线程的loop推出;如果我们在自己创建的线程去创建looper的以后,我们这里传的就是true了。

  public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    
    //我们自己创建的线程准备looper的时候,会调用这个方法,可以看到传了false。
     public static void prepare() {
        prepare(true);
    }

接下来我们分析一下loop()方法。looper方法是一个无限循环的方法,不断去消息队列里取出消息发送给对一个的Handler处理。

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;

        // 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 (;;) {
        //取出一个消息处理,
            Message msg = queue.next(); // might block
            if (msg == null) {
            //没有消息了就退出消息循环。
                // No message indicates that the message queue is quitting.
                return;
            }

            //把消息分发给对应的handler处理。
            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            //把已经处理的消息放回到消息池里面,用来复用这个消息。这个消息吃的最大缓存熟是50。
            msg.recycleUnchecked();
        }
    }

那么消息队列是在什么时候创建的呢?其实是在为每个线程准备Lopper的时候创建的,是在Looper的构造函数里

 private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

从上面的逻辑我们可以看出,每个Looper的创建都会伴随着一个消息队列的产生,也就是不同线程之间的消息队列是不一样的,每个线程都会创建一个消息队列。另外looper里面的一个成员sThreadLocal,这个是ThreadLocal类型的,ThreadLocal这个类会单独为每个线程存储一个本线程单独使用的Looper,线程之间不会共享,也不会相互干扰。

  • 消息循环我们就分析完了,接下来我们分析消息是怎么分发到消息队列里的
  1. 消息的分发是通过下面这样的方式发送出去的。
 Handler mHandler = new Handler(Looper.getMainLooper());
 Message msg = Message.obtain()
 给消息中添加参数的标识。
 msg.what = 1;//这个消息的标识
 msg.ars1 = 2;//这个消息锁携带的数据
 mHandler.sendMessage(msg) //发送消息

可以看出,发送消息需要一个消息载体Message和运送载体的Handler

  • 我们来分析一下Handler的构造函数
 public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
        //这里为我们自定义Handler给出来知道方针,不推荐匿名内部类,成员类局部累;如果非要在一个类的内部使用,最好就是静态的内部类。
            final Class 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.prepare()发导致的
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //拿到当前loop拥有的handler
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
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) {
        //为该message指定处理这个消息的handler,后面在处理这个消息的时候
        //就是在循环中拿到这个handler去处理这个消息,所以这一步很重要。
        msg.target = this;
        //由于我们分析的是无参数的构造mAsynchronous=false
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //消息入队列
        return queue.enqueueMessage(msg, uptimeMillis);
    }

enqueueMessage有两个操作,一个是没有队列的时候构造队列的头,另外一个就是入队操作。

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) {
                //没有队列,创建队列的头
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                
                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;
    }

消息的入队操作介绍完来,下面介绍消息是什么时候分发给处理者的
还记得我们的消息的哪个无限的for循环吗,我再贴一下代码

   for (;;) {
        //取出一个消息处理,
            Message msg = queue.next(); // might block
            if (msg == null) {
            //没有消息了就推出消息循环。
                // No message indicates that the message queue is quitting.
                return;
            }

            //把消息分发给对应的handler处理。
            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            //把已经处理的消息放回到消息池里面,用来复用这个消息。这个消息吃的最大缓存熟是50。
            msg.recycleUnchecked();
        }

msg.target.dispatchMessage(msg);这一行就是去把消息分发给对应的handler处理

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

dispatchMessage分发最终把消息传给来handlerMessage分发,所以我们在创建Handler的时候都要复写handlerMessage方法来处理分发过来的消息;因为我们用的是无参数的
Handler的构造分发,所以mCallback是null,但是如果我们传来mCallback,可以看到回调函数的优先级是比handlerMessage分发的优先级高的。


Android的消息机制我们就介绍完来,下面我们总结一下:

  1. 涉及到的类有:
  • Looper:主要用来创建消息队列和对消息队列进行遍历
  • MessageQueue:主要用来存储Handler发过来的消息
  • Message:消息的载体,主要负责携带我们发送的数据
  • Handler:消息的发送者和执行者。
  1. Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
  2. 下面是Android中的消息机制在的处理流程图
    Android消息机制.png

你可能感兴趣的:(Android源码分析之消息机制)