美文网首页
Android源码分析之消息机制

Android源码分析之消息机制

作者: AN_9c94 | 来源:发表于2019-07-16 20:34 被阅读0次

    说明:本文是基于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<? 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.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源码分析之消息机制

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