【Android】Android中的消息机制——Looper、Handler、MessageQueue与Message

参考链接:https://www.jianshu.com/p/f1da1ed7bd7e
参考链接:https://www.cnblogs.com/neo-java/p/7017843.html

整体流程图:
在这里插入图片描述

1、Looper、Handler、MessageQueue与Message的关系与相关概念

1-1 什么是Android消息处理机制?

消息就是一个通知,用于报告指定事件发生了。

消息驱动是围绕消息的产生与处理展开的,并依靠消息循环机制来实现。

Android和Windows一样,也是消息驱动型的系统。

引用一下消息驱动机制的四要素:
  ①接收消息的“消息队列”
  ②阻塞式地从消息队列中接收消息并进行处理的“线程”
  ③可发送的“消息的格式”
  ④“消息发送函数”

与之对应,Android系统中对应实现了:
  ①接收消息的“消息队列” —— MessageQueue
  ②阻塞式地从消息队列中接收消息并进行处理的“线程” —— Thread+Looper
  ③可发送的“消息的格式” —— Message
  ④“消息发送函数”—— Handler的post()和sendMessage()
  
Android有大量的消息驱动方式来进行交互,比如Android的四大组件——Activity, Service, Broadcast, ContentProvider的启动过程的交互,都离不开消息机制。

1-2 Handler

Android的消息机制,很多时候我们也称之为“Handler机制”,可见Handler这个东西是相当重要了~~那么Handler是用来干什么的呢?

初学者以为Handler就是用来更新UI的。

如果你也这么认为,那么从今天起你就要放弃这种狭隘的想法——Handler是Android消息机制的上层接口,因此我们在开发中与Handler打交道的机会最多。Handler并不是专门用来更新UI的,只是开发者常常用它来更新UI。Handler的主要用于同一个进程间的线程通信,Handler用于更新UI的时候是"子线程与主线程通信";当然,Handler也可以用于子线程之间通信。
  
Handler的消息机制主要是就指“Handler的运行机制”,Handler的运行机制是需要底层的MessageQueue和Looper支持的。

1-3 MessageQueue

MessageQueue翻译过来是"消息队列"的意思,实际上它内部的数据结构不是队列,而是单向链表

MessageQueue中储存了大量的消息,由于一个线程同一时间只能处理一条消息,所以我们建了一个链表,将我们需要处理的消息按顺序储存起来,然后一项一项的交给需要的线程处理,这就是MessageQueue存在的价值。

为什么MessageQueue要用链表而不用数组来作为数据结构呢?单链表更适合做增删的操作,数组更适合做随机访问的操作。在MessageQueue中,我们不只要做随机访问(这里不是真的随机访问,消息的读取是根据计算出来的时间顺序来的,后文会讲),我们做的更多的是插入和删除操作
MessageQueue.enqueueMessage()就是插入消息(消息的插入需要根据时间发送的时间顺序来确定,不存在头插还是尾插),而MessageQueue.next()则是读取消息,且读取的同时也伴随删除的过程。试想一下,一个消息队列要循环起来,必然要频繁的进行插入/读取操作,假如采用数组的话,这两个操作的平均时间复杂度都是O(N/2),而链表为O(1),显然链表更合适。

1-4 Looper

Looper和MessageQueue的消息就像水泵和井(里边装的是水)的关系一样,我们有了消息(水),但是为了把水从井中抽取出来(循环起来),我们得有一个水泵作为动力,这个动力就是Looper。
  
如果我们在一个线程中调用Looper.prepare()…Looper.loop(),那么你的线程就成功升级为了一个Looper线程,这意味着你的线程有了一个消息泵(Looper)和一个消息队列(MessageQueue),此时你就可以调用Handler来进行线程间的通信了。

我们应用的UI线程也就是主线程,在应用启动的时候,系统会自动初始化一个Looper,也就是说,我们的UI线程默认是Looper线程。这也就是为什么主线程中直接调用Handler没什么事,但在子线程中创建Handler前需要手动调用Looper.prepare()…Looper.loop()的原因。

1-5 Message

Message也就是消息,井中的水。一个Message包括了消息类型(what),消息内容(arg1,arg2),发送它的Handler(target),Runnable回调接口等:

public int what;        数据类型
public int arg1;        简单的整数值
public int arg2;        简单的整数值可以直接发送,是一种替代setData(Bundle)的低成本方案,更加省资源
public Object obj;
......
/*package*/ int flags;
/*package*/ long when;          Handler发送一个消息之后,返回此消息的目标交付时间(以毫秒为单位)。
/*package*/ Bundle data;        Bundle可以携带更复杂的数据类型
/*package*/ Handler target;     哪个Handler发送的消息
/*package*/ Runnable callback;

可以看到,Message带了一个指向一下个节点的链,也就是说,MessageQueue内部维护的实际上是一个链表:
/*package*/ Message next;

private static final Object sPoolSync = new Object();
private static Message sPool;      消息池
private static int sPoolSize = 0;

private static final int MAX_POOL_SIZE = 50;    消息池的最大容量

讲到这里,我们先上一张图加深一下大家对于这几个东西的直观认识:

在这里插入图片描述

2、子线程与主线程Handler通信原理(子线程是如何通过Handler更新UI的)

2-1 一些熟悉的场景

private Handler handler = new Handler(){
    
    
  @Override
  public void handleMessage(Message msg) {
    
    
      super.handleMessage(msg);
      switch (msg.what) {
    
          //判断标志位
          case 1:
              更新UI操作
              break;
      }
   }
 };
public class MyThread extends Thread {
    
    
    @Override
       public void run() {
    
    
         super.run();
         耗时操作
       }
     Message msg = Message.obtain();	从全局池中返回一个message实例,避免多次创建(如new Message)
     msg.obj = data;
     msg.what = 1;   			标志消息的标志
     handler.sendMessage(msg);
  }

  new MyThread().start();

上面代码是我们在Android开发中经常写的一段代码,其主要作用是在子线程中进行耗时操作,并通过Handler向主线程中发送消息,通知主线程做出相应的UI变化。注意这段代码中,Handler是在主线程中创建的,因此不需要手动调用Looper.prepare()添加Looper。
  
如果我们试图在子线程中创建一个Handler,如:

Handler handler;
public class MyThread extends Thread {
    
    
 @Override
    public void run() {
    
    
      super.run();
      耗时操作
    }

  handler = new Handler();
  Message msg = Message.obtain();从全局池中返回一个message实例,避免多次创建(如new Message)
  msg.obj = data;
  msg.what = 1;   标志消息的标志
  handler.sendMessage(msg);
}

new MyThread().start();

那么很显然,这个时候就会出bug了~~为了解决这个bug,我们需要手动在子线程中创建Looper:

handler = new Handler();
Looper.prepare(); ——————————————————添加语句1/2
Message msg = Message.obtain();
msg.obj = data;
msg.what = 1;   
handler.sendMessage(msg);
Looper.loop(); ——————————————————————添加语句2/2

为什么是这样呢?接下来从源码的解读分析

2-2 Looper

(frameworks/base/core/java/android/os/Looper)

(1)Looper.perpare()
public final class Looper {
    
    
    private static final String TAG = "Looper";

    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;
    final Thread mThread;

    public static void prepare() {
    
    
        prepare(true);
    }

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

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

首先我们看到Looper.prepare()方法中调用了prepare(ture)方法,其中true表示,该Looper是可以被终止的。因为我们是在子线程中创建的Looper,当子线程的消息处理完之后,理应把改Looper终止掉(MessageQueue.quit)。但是在主线程的Looper中:

public static void prepareMainLooper() {
    
    
     prepare(false);
     ......
}

prepareMainLooper就是给主线程添加Looper,可以看到,主线程中的prepare(false)中的参数false表示的是,主线程中的Looper不能被终止掉,毕竟它是整个应用的生命,需要时刻准备着处理或者正在处理应用中的各种消息。
  
好了我们接着看上面的prepare()中干了什么。这里出现了一个新的重量级的东西:sThreadLocal,它是ThreadLocal类的实例,关于ThreadLocal类是干什么的,我们在这里不做过多解释,我们只需要知道他是用于储存不同线程唯一对象的一个东西,即多个线程在ThreadLocal类中,通过ThreadLocal.set()保存了自己的变量之后,那么我们在各个子线程中调用ThreadLocal.get()方法,得到的仍然是当前线程之前存进去的那个值。各个线程存取各自的值,不会产生冲突。

知道了ThreadLocal的作用之后,我们在来看sThreadLocal.set(new Looper(quitAllowed));这句表示我们在ThreadLocal类中保存了一个Looper对象(new Looper()),根据上面对ThreadLocal类的介绍,如果我们再当前线程中调用ThreadLocal.get()方法,则会得到本线程之前保存的唯一的变量。因此:

if (sThreadLocal.get() != null) {
    
    
    throw new RuntimeException("Only one Looper may be created per thread");
}

这个if表示的是,如果sThreadLocal.get() != null,说明当前线程中已经存在一个Looper,我们不能在一个线程中同时创建多个Looper,所以此时会抛出异常。为了避免这种异常,我们可以在Looper.prepare()之前调用Looper.myLooper()类来返回当前线程中的Looper对象,判断为空之后,再调用prepare():

public static @Nullable Looper myLooper() {
    
    
    return sThreadLocal.get();
}

Ok,比比了这么多,我们接着看new Looper()中Looper中的构造函数中做了什么:

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

这里出现了消息队列MessageQueue(quitAllowed),其中的参数就是表示是否允许Looper退出的标示符。可以看到,在Looper中我们创建了一个MessageQueue实例:

MessageQueue(boolean quitAllowed) {
    
    
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

此时我们的Looper就拥有了MessageQueue的对象引用。

(2)Looper.Loop()

在Looper.perpare()调用完,即我们为线程准备好Looper之后,最后一步我们还需要调用Loop()让整个Looper循环起来,这样消息才能发送出去:

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) {
    
    
            return;
        }
        ......
        msg.target.dispatchMessage(msg);
        ......
        msg.recycleUnchecked();     //释放占据的资源
    }
}

首先调用了我们刚说过的myLooper()来获取当前线程在ThreadLcoal类中储存的Looper,如果Looper为空则会抛出异常,提示当前线程没有Looper。然后final MessageQueue queue = me.mQueue;是获取当前线程的Looper中的MessageQueue对象。
  
之后这个Looper便进入了一个无限循环的状态——for(;;)Message msg = queue.next();是一条一条遍历整个消息队列,拿出msg消息。
  
msg.target.dispatchMessage(msg);这句中的msg.target实际上就是当前消息的目标Handler,也就是哪个线程中的Handler发送的消息,当然,这个发送它的Handler也要在自己所在的线程中接受这条消息。
  
msg.recycleUnchecked();这句是将这条消息放入Message类中的消息池中。

从上面我们基本上可以得出——Looper > MessageQueue > Message的关系,也就是说,每个Looper中维护了一个消息队列,而一个消息队列中则以链表的形式排列着一条条消息。

2-3 MessageQueue.next()

(frameworks/base/core/java/android/os/MessageQueue)
上面的Looper类中我们实例化了一个MessageQueue对象,并调用了MessageQueue.next()类方法:

Message next() {
    
    
    final long ptr = mPtr;      当消息循环已经退出,则直接返回
    if (ptr == 0) {
    
    
        return null;
    }

    int pendingIdleHandlerCount = -1;  循环迭代的首次为-1,也就是初始值为-int nextPollTimeoutMillis = 0;
    for (;;) {
    
          消息队列开始无限循环
        if (nextPollTimeoutMillis != 0) {
    
    
            Binder.flushPendingCommands();
        }

        阻塞操作的方法,当等待nextPollTimeoutMillis时长,或者消息队列里边有了消息被唤醒时,
        只有满足这两个条件该方法才会返回,for循环才能往下执行下面的代码,否则就一直在这等着
        nativePollOnce(ptr, nextPollTimeoutMillis);

		这一段同步块代码中就是在检索下一条message,如果找到了就返回
        synchronized (this) {
    
       
        	
        	手机开机到现在的时间(毫秒为单位),手机睡眠的时间不包括再内
            final long now = SystemClock.uptimeMillis();    
            Message prevMsg = null;
            Message msg = mMessages;
			
			如果此时消息不为null,但是这个消息找不到发送它的Handler,说明为不合法消息,
			放弃并寻找下一条异步消息
            if (msg != null && msg.target == null) {
    
    
                do {
    
              
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
			
			如果消息不为null(这次是个正常消息)
            if (msg != null) {
    
      
                if (now < msg.when) {
    
    
                    
                    虽然消息是正常的,但是还没到发送的时间。msg.when表示消息发送的时间,因为我们可能调用了postDelay()
                    延迟发送。我们之前说过,当等待nextPollTimeoutMillis时长后nativePollOnce()方法就会返回。
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
    
        
					
					此时表示成功抓到了一条消息
                    mBlocked = false;
                    
                    if (prevMsg != null) {
    
          
                        prevMsg.next = msg.next;  单链表操作,下面讲
                    } else {
    
    
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    msg.markInUse();
                    return msg;
                }
            } else {
    
    
                nextPollTimeoutMillis = -1;
            }
            ......
        }
        ......
    }
}

首先:

final long ptr = mPtr;
if (ptr == 0) {
    
    
    return null;
}

这个mPtr我们之前在MessageQueue的构造方法中提到过:

MessageQueue(boolean quitAllowed) {
    
    
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

它通过mPtr = nativeInit();得到值,nativeInit()是一个native层的方法,根据注释来看,它主要是判断Looper目前的状态。if (ptr == 0) { 表示的是当前线程的Looper已经退出或者被处理掉了,这种情况发生在“应用在退出之后试图重启Looper”的情况下,这种情况是不被允许的,因此此时return null;
  
上面这段中还要讲的就是单链表的操作:

if (prevMsg != null) {
    
    
        prevMsg.next = msg.next;
    } else {
    
    
        mMessages = msg.next;
    }
    msg.next = null;
    msg.markInUse();    表示这个消息已经被传递(使用了)
    return msg;
}

首先,在synchronized (this)同步块一开始的时候,有一个全局变量mMessages,这实际上就是等待处理消息,这个变量很重要,之后我们会多次遇到。这里还有一个叫prevMsg的Message,用于保存找不到msg.target的消息(废消息)。OK,回到链表操作中来,if (prevMsg != null),很遗憾,有一个不合法的消息,此时上面已经经历了prevMsg = msg; msg = msg.next;这两步,加上prevMsg.next = msg.next;这一步,实际上就是prevMsg的后继引用跳过了msg,直接指向了msg的下一位(注意prevMsg是不合法的,但是它的下一位msg是合法的)。
  
再看else,说明没有不合法的消息,Message msg = mMessages;加上mMessages = msg.next;,这两步实际上和上面一样,也是工作指针后移,越过了msg。也就是msg出队,msg的下一条消息成为mMessages(待处理消息),否则就进入一个阻塞状态,一直等到有新的消息入队。
  
OK,上面if和else中越过的msg,就是我们要返回的正常消息,接着两句:msg.next = null;return msg;就可以知道,前者是将msg的后继引用清空(将它从链表中删除),然后return。

通过上面的分析我们知道,MessageQueue.next()作用就是遍历链表,找出一个合法的msg,将它从链表中删除后返回,这实际上也就是一个消息出队的过程。

2-4 Handler

(1)Handler.post/sendMessage将一个消息添加到消息队列中

上面我们已经讲完了Handler通信的两个重要的基础类——MessageQueueLooper,接下来我们分析一下Handler是如何将一个消息发送出去的。

我们从Handler的构造函数开始看起:

public Handler() {
    
    
    this(null, false);
}
......
public Handler(Callback callback, boolean async) {
    
    
    ......

    mLooper = Looper.myLooper();
    if (mLooper == null) {
    
    
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

可以看到,Hnadler在发送消息的时候先获取当前线程的Looper,然后做一次Looper的非空判断;接着获取了Looper中的MessageQueue对象。这样,我们的Handler已经和Looper以及MessageQueue取得了联系。
  
接着回到文章最开始的时候举的那个例子中,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");
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

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

上面几个方法的逻辑都比较清晰,可以看到,最终调用了MessageQueue的enqueueMessage()方法,其中第一个参数为msg,第二个参数uptimeMillis = SystemClock.uptimeMillis() + delayMillis,也就是开机到现在的时间(不包括睡眠时间)+我们设定的delay时间,接下来我们看看enqueueMessage()方法:

boolean enqueueMessage(Message msg, long when) {
    
    
    if (msg.target == null) {
    
        目标Handler为null
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
    
        msg已经用过了  
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
    
    
        if (mQuitting) {
    
            
        	
        	如果这个Looper正在退出,回收msg,加入到消息池
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            
            释放msg资源
            msg.recycle();     
            return false;
        }

		表示这个msg已经被使用了
        msg.markInUse();     
        
        这里我们可以知道,Message类的when属性实际上就是SystemClock.uptimeMillis() + delayMillis
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
		
		如果p == null,说明MessageQueue中没有消息;或者msg的时间是这个消息队列中最靠前的
        if (p == null || when == 0 || when < p.when) {
    
    
        
            msg.next = p;      		将这个msg提取出来,并复制给mMessages,mMessages会在上面的
            mMessages = msg;    	在MessageQueue.next()方法中进行一系列判断后返回
            needWake = mBlocked;    当阻塞时需要唤醒
        } else {
    
            说明此时我们发送过来的消息需要按照一定规则插入到队列中
            
            将消息按时间顺序插入到MessageQueue。一般地,不需要唤醒事件队列,除非
            消息队头存在barrier,并且同时Message是队列中最早的异步消息。
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;for (;;) {
    
    
                prev = p;
                p = p.next;     					工作指针后移并且循环遍历链表
                if (p == null || when < p.when) {
    
       找到一个不为空并且发送时间大于当前发送时间的消息,
                    break;      					跳出循环,准备把msg(要发送的消息)插到这条消息之前
                }
                if (needWake && p.isAsynchronous()) {
    
    
                    needWake = false;
                }
            }
   ②        msg.next = p;     单链表插入
            prev.next = msg;
        }

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

MessageQueue是按照Message触发时间的先后顺序排列的,队头的消息是将要最早触发的消息。当有消息需要加入消息队列时,会从队列头开始遍历,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序。

上面代码中①处的for()循环就是在循环遍历MessageQueue以找到合适的msg插位置,②处的代码实际上就是一个单链表插入的过程,我们可以把整个插入的代码连起来,这样更容易看出:

prev = p;
p = p.next;
msg.next = p;
prev.next = msg;

没骗你吧~~这就是把我们的要发送的消息msg插入到了链表中的p节点的下一个节点(此处与原文作者表述不同)。

我们需要明确的一点是,这里的Handler.post/sendMessage方法是和上面的MessageQueue.next()方法是对应的,他是消息的入队操作。

(2)Looper.loop()中调用Handler.dispatchMessage()接收并处理消息

好了,讲到这里,我们可以看到,Handler.post或者Handler.sendMessage方法,最终是将他们要发送的消息添加到了消息队列中(Handler.post实际上也是调用了Handler.sendMessageDaley())。

那么接收消息在哪呢?好吧看标题你也知道,Looper.loop()方法中我们实现了消息的重写与接收。我们回过头去看Looper类,在该类中MessageQueue.next()这个消息出队的方法调用完之后,出现了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);
        }
    }

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    
    
    }

可以看到最终我们调用了一个空方法,为什么呢,因为消息的最终回调是由我们控制的,我们在创建handler的时候会在Handler所在的线程中(如果是子线程更新主线程UI的话,就在主线程中)重写handleMessage方法,然后根据msg.what进行消息处理(对照开始给出的Hanlder常见场景)。

我们梳理一下整个流程:

① handler = new Handler();  创建Handler对象
② Looper.prepare();     	准备好Looper,初始化MessageQueue
③ Message msg = Message.obtain();    从消息池中取出一个可用的Message实例
④ msg.obj = data;           消息的数据
  msg.what = 1;             标志消息的标志
⑤ handler.sendMessage(msg); 发送消息,将一个消息添加到消息队列中去
⑥ Looper.loop();        	使用loop使消息队列循环起来,并进行消息的出队删除操作

Looper.loop()中的消息出队之后,将调用Handler的dispatchMessage,最终我们在代码中重写的handleMessage用以自定义处理:

    private Handler handler = new Handler(){
    
    
       @Override
       public void handleMessage(Message msg) {
    
    
           super.handleMessage(msg);
           switch (msg.what) {
    
          判断标志位
               case 1:
                   更新UI操作
                   break;
           }
        }
      };
(3)Handler.post() & View.post() & Activity.runOnUiThread()

① Handler.post()
  
上面我们说了Handler.sendMessage()方法,并且在文章开头的实例中展示了Handler.sendMessage()的使用方法,下面我们来说一下Handler.post(),该方法的具体使用还是略微的有一点不同的:

    public final boolean post(Runnable r){
    
    
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

可以看到,Hander.post()方法还是调用了sendMessageDelayed方法,这跟之前的流程是一样的。这里我们要说的是sendMessageDelayed(getPostMessage(r), 0);中的getPostMessage(r)方法:

    private static Message getPostMessage(Runnable r) {
    
    
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

在这个方法中将消息的callback字段的值指定为传入的Runnable对象。这个callback字段之前我们有遇到过——在Handler的dispatchMessage()方法中原来有做一个检查,如果Message的callback等于null才会去调用handleMessage()方法,否则就调用handleCallback()方法:

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

handleCallback(msg):

    private static void handleCallback(Message message) {
    
    
        message.callback.run();
    }

这里的run也就是一开始的时候传入的run接口。也就是说,如果通过post(Runnable r)传递消息的话,我们直接就可以在post()方法中进行UI操作:

    handler = new Handler();  
        new Thread(new Runnable() {
    
      
            @Override  
            public void run() {
    
      
                handler.post(new Runnable() {
    
      
                    @Override  
                    public void run() {
    
      
                        在这里进行UI操作  
                    }  
                });  
            }  
        }).start();  

写法上简洁了很多,但是本质上都是一样的。

② View.post()
(source/android-24/android/view/View):

    public boolean post(Runnable action) {
    
    
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
    
    
            return attachInfo.mHandler.post(action);
        }

        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }

可以看到同样调用了Handler的post方法

③ Activity.runOnUiThread()

    public final void runOnUiThread(Runnable action) {
    
    
        if (Thread.currentThread() != mUiThread) {
    
    
            mHandler.post(action);
        } else {
    
    
            action.run();
        }
    }

如果当前的线程不等于UI线程(主线程),就去调用Handler的post()方法,否则就直接调用Runnable对象的run()方法。

2-5 Message.obtain

上面我们需要解释的一个东西便是Message msg = Message.obtain();这句,Message的obtain()方法中维护了一个消息池,其最大容量MAX_POOL_SIZE = 50

    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
    
    
        synchronized (sPoolSync) {
    
    
            if (sPool != null) {
    
        消息池不为空时从消息池中直接拿消息
                Message m = sPool;
                sPool = m.next; 	工作指针后移,准备取出sPool
                m.next = null;  	从sPool中取出一个Message对象,并且消息链表断开
                m.flags = 0;    	清除in-use flag
                sPoolSize--;    	消息池的可用大小进行减1操作
                return m;
            }
        }
        return new Message();   	当消息池为空时,直接创建Message对象
    }

    /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
    */
    public Message() {
    
    
    }

我们当然可以直接new Message()这样来创建消息,但是一般来讲我们还是应该调用Message.obtain()方法来返回一个消息实例,以避免Message对象的多次创建。

好了,到此为止,我们调用Handler发送消息更新UI的整个流程就说完了。

3、Activity启动过程中UI线程的MainLooper的创建

Actvity的启动流程我们之前在一片文章中有讲过,真正启动Activity的是ActivityThread类中的main()方法:

    public static void main(String[] args) {
    
    
        ......
        Looper.prepareMainLooper();

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

        ......
        Looper.loop();
        ......
    }

可以看到,我们在启动一个Activity之前,我们在ActivityThread.main()方法中,调用了Looper.prepareMainLooper() 方法:

    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.prepare()方法一样,只不过这里准备的是主线程中的Looper,因此prepare(false); 其中的参数false表示的是,该线程的Looper不能退出。之后调用myLooper则是获取主线程中的Looper对象,这些和上面都没什么区别。

我们回到ActivityThread.main()中,在准备完主线程的Looper之后,ActivityThread thread = new ActivityThread();创建一个ActivityThread实例。thread.attach(false);参数false表示这不是系统进程,是给普通应用程序使用的进程。

我们接着thread.attach(false);来看:

    private void attach(boolean system) {
    
    
        sCurrentActivityThread = this;
        mSystemThread = system;
        if (!system) {
    
    
            ViewRootImpl.addFirstDrawHandler(new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    ensureJitEnabled();
                }
            });
            ......
            final IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
    
    
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
    
    
                throw ex.rethrowFromSystemServer();
            }
            ......

注意到mgr.attachApplication(mAppThread);这句,其中mAppThread是ApplicationThread的对象,mgrIActivityManager接口类,而真正实现IActivityManager接口的是在ActivityManagerService(AMS)类中,关于AMS类中的代码我们这里就先不详细解释了,我们只需要知道,在它当中经过一系列的处理~~最终:

app.thread.scheduleLaunchActivity(new Intent(r.intent), r, System.identityHashCode(r),
        r.info, r.icicle, results, newIntents, !andResume, isNextTransitionForward());
}

又回调到了ActivityThread类中:

    该方法在ApplicationThread(Binder线程)中调用
    @Override
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
    
    

            updateProcessState(procState, false);

            ActivityClientRecord r = new ActivityClientRecord();

            r.token = token;
            r.ident = ident;
            r.intent = intent;
            r.referrer = referrer;
            r.voiceInteractor = voiceInteractor;
            r.activityInfo = info;
            r.compatInfo = compatInfo;
            r.state = state;
            r.persistentState = persistentState;

            r.pendingResults = pendingResults;
            r.pendingIntents = pendingNewIntents;

            r.startsNotResumed = notResumed;
            r.isForward = isForward;

            r.profilerInfo = profilerInfo;

            r.overrideConfig = overrideConfig;
            updatePendingConfiguration(curConfig);

            sendMessage(H.LAUNCH_ACTIVITY, r);
        }

可以看到,这里将一系列应用的信息封装在ActivityClientRecord中之后,最终调用了sendMessage(H.LAUNCH_ACTIVITY, r);发送消息:

    private void sendMessage(int what, Object obj) {
    
    
        sendMessage(what, obj, 0, 0, false);
    }
    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
    
    
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
    
    
            msg.setAsynchronous(true);
        }
        mH.sendMessage(msg);
    }

上面的“H”实际上就是一个Handler类:private class H extends Handler { ,而这里的mH则是H的子类。接着往下看:

    public void handleMessage(Message msg) {
    
    
            switch (msg.what) {
    
    
                case LAUNCH_ACTIVITY: {
    
    
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    ......

可以看到,这里handleMessage收到了我们发送的LAUNCH_ACTIVITY也就是启动Activity的请求,实际上这里还有PAUSE_ACTIVITYRESUME_ACTIVITY等一系列请求的处理。

之后handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");–>performLaunchActivity–>mInstrumentation.callActivityOnCreate一系列流程之后,我们的Activity就启动了,这个我们在之前的文章中有讲过,这里不再獒述。

给大家展示这一段,主要是为了说明,在Activity的启动过程中,在系统层面也是调用Handler机制来进行一系列事件的处理,从而推动系统的循环进行。

站在巨人的肩膀上摘苹果:
《Android开发艺术探索》
郭霖 Android异步消息处理机制完全解析,带你从源码的角度彻底理解
Gityuan Android消息机制1-Handler(Java层)

猜你喜欢

转载自blog.csdn.net/qq_30885821/article/details/108893823